OverviewBasic UsageAdditional functionalityLimitations

The JS Painter implementation permits overriding any UI object's painting method with one written in Javascript. It employs the same mgraphics API which may be used from the jsui object with a few additions. This provides the advantage over using the jsui object for custom user interface in that it permits the standard threading behavior of Max objects (i.e. processing scheduler events in the high priority scheduler thread, and painting in the low priority main application thread), rather than forcing all object logic to be executed in the low priority main application thread as is the case for the jsui object. So you can have a custom dial or slider interface for example, and if numbers are passing through the object at high priority, their timing will not be adversely affected as they would with the jsui object.

To override the object's paint method, simply create a javascript file in the max search path, define a javascript function (method) named "paint", and set the object's jspainterfile attribute to the file's filename. In your paint method, you may query the underlying native C object's value from the box.getvalueof() method, and any of the underlying native C object's attributes via the box.getattr("attributename") method to inform drawing. Otherwise, your paint method should look similar to how you might program a custom UI object for jsui. Below is an example of how you could program in javascript an interface that looks virtually identical to Max's native toggle object:

function paint() {
  var val = box.getvalueof()[0] // this is an array of size 1
  var viewsize = mgraphics.size
  var valrange = box.getattr("size")
  var width = viewsize[0]
  var height = viewsize[1]
  var start

  mgraphics.set_source_rgba(box.getattr("bgcolor"))
  mgraphics.rectangle(0, 0, width, height)
  mgraphics.fill()

  if (val) {
    mgraphics.set_source_rgba(box.getattr("checkedcolor"))
  } else {
    mgraphics.set_source_rgba(box.getattr("uncheckedcolor"))
  }

  mgraphics.set_line_width((2 / 12) * box.getattr("thickness") * 0.01 * width) // top left to bottom right, thin
  mgraphics.set_line_cap("square")

  start = (7 / 24) * width

  mgraphics.move_to(start, start)
  mgraphics.line_to(width - start, height - start)
  mgraphics.stroke()

  mgraphics.move_to(width - start, start)
  mgraphics.line_to(start, height - start)
  mgraphics.stroke()
}

In addition to the standard mgraphics functionality, you may also call the underlying native C object's paint method to render an object's standard UI and add interface elements on top of it using the mgraphics.parentpaint() method. For example, here's a way to render a border on top of the standard native object UI using the underlying object's "elementcolor" style color:

function paint() {
  var viewsize = mgraphics.size
  var width = viewsize[0]
  var height = viewsize[1]

  // call original object paint method
  mgraphics.parentpaint()

  // the actual underlying attribute name may be different
  // so we use the attrname_forstylemap() method to map
  // the style color to object attribute name
  var colorname = box.attrname_forstylemap("elementcolor")
  var bordercolor = box.getattr(colorname)

  // draw border rectangle over it
  mgraphics.set_source_rgba(bordercolor)
  mgraphics.rectangle(0.5, 0.5, width - 1, height - 1)
  mgraphics.stroke()
}

At the time of writing, there are some limitations to what you may do with the JS Painter implementation. Firstly, there is no access to mousing events and logic. All such event processing will remain handled by the native C object. Also, any use of the Task object to support timing information in the UI is not supported. Also, objects with text fields will have some issues. Text may be rendered with standard mgraphics API calls, but as is the case with JSUI, there is no automatic linewrapping, and if the underlying object has a textfield (as do number boxes, message objects, comment objects and the standard text object), there will be conflicts or missing text. For this reason the jspainterfile attribute is hidden for such objects. And finally, there is no support for autowatch or double clicking to open the jspainterfile in an in app editor.

See Also

Name Description
JavaScript Usage JavaScript Usage