Using SVG files with JSUI
Hi
One badly documented topic in Max is the usage of SVG images for userinterfaces. It took me a moment to figure this out:
mgraphics.init();
mgraphics.relative_coords = 0;
mgraphics.autofill = 0;
var g_icon;
var defaultColor = [0., 0., 0., 1.];
var myval = 0;
var pressColor = [1., 0., 0., 1.]
var releaseColor = [0., 1., 0., 1.]
// process arguments
if (jsarguments.length>1)
g_icon = new MGraphicsSVG(jsarguments[1]);
function bang()
{
myval = 1 - myval; // toggle 0/1
mgraphics.redraw();
}
function paint()
{
gc(),
g_icon.mapcolor(defaultColor, (myval == 0)?releaseColor:pressColor);
mgraphics.translate(1,1);
mgraphics.scale(1.,1.);
mgraphics.svg_render(g_icon);
}
function onclick()
{
bang();
outlet(0, myval);
}
onclick.local = 1; //private
this is a simple code for a jsui toggle using a SVG-file (with black lines) to use as an image for toggling between two colors.
all you need to provide in the 'jsarguments' attribute is the filepath to the SVG.
I hope this helps some of you out there that don't like the [pictctrl] object as I do.
here another variation, instead providing two svg to toggle between two states:
mgraphics.init();
mgraphics.relative_coords = 0;
mgraphics.autofill = 0;
var g_icon_off;
var g_icon_on;
var myToggle = 0;
var myScale = 1.
// process arguments
if (jsarguments.length>1)
g_icon_off = new MGraphicsSVG(jsarguments[1]);
if (jsarguments.length>2)
g_icon_on = new MGraphicsSVG(jsarguments[2]);
onresize();
function bang()
{
myToggle = 1 - myToggle;
mgraphics.redraw();
notifyclients();
}
function paint()
{
gc();
mgraphics.translate(3,1);
mgraphics.scale(myScale, myScale);
mgraphics.svg_render((myToggle == 0)?g_icon_off:g_icon_on);
}
function onclick()
{
bang();
outlet(0, myToggle);
}
onclick.local = 1; //private
function onresize()
{
myScale = Math.min(this.box.rect[2] - this.box.rect[0], this.box.rect[3] - this.box.rect[1]) / Math.max(g_icon_on.size[0],g_icon_on.size[1]);
}
onresize.local = 1;
function getvalueof () {
return myToggle
}
function setvalueof (val) {
myToggle = val;
}
thanks for sharing!
No worries. But I just realized this approach is not scaling when using presentation mode. Any hints?
IIRC for presentation rect stuff with jsui you can use this.box.getattr(“presentation_rect”) to determine the correct bounds/image size. I think you can now also configure a MaxObjlistener to observe presentation_rect attribute (to simulate the effect onresize() gets you in patching mode).
Is there a way to detect presentation mode of the parent patcher from within js?
thanks to the getattr() function there is a way to query the presentation mode of the parent patcher. Unfortunately the onresize() function is not triggering in presentation mode and the presentation attribute of the parent patcher is not working with listeners, but I think I found a way to keep the housekeeping functions to a minimum:
// Created my maybites.ch (2021) - License: Free to use and hack. happy patching.
mgraphics.init();
mgraphics.relative_coords = 0;
mgraphics.autofill = 0;
var g_icon_off;
var g_icon_on;
var myToggle = 0;
var myScale = 1.
var myOffset = [0., 0.];
var hasResized = false;
var presentation = 0;
declareattribute("offset","getmyOffset","setmyOffset");
// listeners on the rect values to keep track on changes
// reason: onresize() is not working in presentation mode
var l1 = new MaxobjListener(this, "patching_rect", rect_changed);
var l2 = new MaxobjListener(this, "presentation_rect", rect_changed);
// process arguments
if (jsarguments.length>1)
g_icon_off = new MGraphicsSVG(jsarguments[1]);
if (jsarguments.length>2)
g_icon_on = new MGraphicsSVG(jsarguments[2]);
rescale();
// updating the gui and output.
function bang()
{
mgraphics.redraw();
outlet(0, myToggle);
}
// drawing function
function paint()
{
gc();
check_mode();
rescale();
mgraphics.translate(myOffset);
mgraphics.scale(myScale, myScale);
mgraphics.svg_render((myToggle == 0)?g_icon_off:g_icon_on);
}
// wating for a mouseclick
function onclick()
{
myToggle = 1 - myToggle;
notifyclients();
bang();
}
onclick.local = 1; //private
// called by rect listeners
function rect_changed(data){
hasResized = true;
}
// check presentation mode
function check_mode(){
if(presentation != this.patcher.getattr("presentation")){
presentation = this.patcher.getattr("presentation");
hasResized = true;
}
}
// calculate new scale depending on mode
function rescale()
{
if(hasResized){
var boxRect = this.box.getattr("patching_rect");
if(this.patcher.getattr("presentation") == 1){
boxRect = this.box.getattr("presentation_rect");
}
myScale = Math.min(boxRect[2], boxRect[3]) / Math.max(g_icon_on.size[0],g_icon_on.size[1]);
}
hasResized = false;
}
// get function for pattr system
function getvalueof () {
return myToggle
}
// set function for pattr sysemt
function setvalueof (val) {
myToggle = val;
bang();
}
// attribute setter
function setmyOffset(posX, posY){
myOffset = [posX, posY];
}
//attribute getter
function getmyOffset(){
return myOffset;
}