How can I pass a custom parameter to BatchPlay?

I have a simple UXP plugin that uses BatchPlay to toggle transformsSnapToPixels, (Vector Pixel Snapping).

const {executeAsModal} = require("photoshop").core;
const {batchPlay} = require("photoshop").action;
const app = require('photoshop').app

async function transformsToogleSnapToPixels(toggle) {
   const result = await batchPlay(
      [
         {
            _obj: "set",
            _target: [
               {
                  _ref: "property",
                  _property: "generalPreferences"
               },
               {
                  _ref: "application",
                  _enum: "ordinal",
                  _value: "targetEnum"
               }
            ],
            to: {
               _obj: "generalPreferences",
               transformsSnapToPixels: toggle,
               legacyPathDrag: true
            },
            _options: {
               dialogOptions: "dontDisplay"
            }
         }
      ],
      {
         synchronousExecution: false,
         modalBehavior: "wait"
      }
   );
}

async function btnOpenNeuralFilters_Click() {
   await executeAsModal(openNeuralFilters, {"commandName": "openNeuralFilters"});
}

async function btnDisableSnapToPixels_Click() {
   await executeAsModal(transformsToogleSnapToPixels, {"commandName": "Disable Snap to Pixels", "toggle": "false"});
}

async function btnEnableSnapToPixels_Click() {
   await executeAsModal(transformsToogleSnapToPixels, {"commandName": "Enable Snap to Pixels", "toggle": "true"});
}

document.getElementById("btnEnableSnapToPixels").addEventListener("click", btnEnableSnapToPixels_Click);
document.getElementById("btnDisableSnapToPixels").addEventListener("click", btnDisableSnapToPixels_Click);

I am trying to use toggle as a custom parameter to feed to transformsToogleSnapToPixels so I don’t need to duplicate functions for toggling snapping on/off. Is this possible? I am aiming for a single function: transformsToogleSnapToPixels to toggle the preference on and off with an argument.

Any help or guidance greatly appreciated.

Change it to modalBehavior: "execute"

Hi Jarda,

Still not working. I’ve tried:

const {executeAsModal} = require("photoshop").core;
const {batchPlay} = require("photoshop").action;
const app = require('photoshop').app

async function toggleSnapToPixels(toggle) {
   const result = await batchPlay(
      [
         {
            _obj: "set",
            _target: [
               {
                  _ref: "property",
                  _property: "generalPreferences"
               },
               {
                  _ref: "application",
                  _enum: "ordinal",
                  _value: "targetEnum"
               }
            ],
            to: {
               _obj: "generalPreferences",
               transformsSnapToPixels: toggle,
               legacyPathDrag: true
            },
            _options: {
               dialogOptions: "dontDisplay"
            }
         }
      ],
      {
         synchronousExecution: false,
         modalBehavior: "execute"
      }
   );
}

async function btnDisableSnapToPixels_Click() {
   await executeAsModal(toggleSnapToPixels, {"commandName": "Disable Snap to Pixels", "toggle": false});
}

async function btnEnableSnapToPixels_Click() {
   await executeAsModal(toggleSnapToPixels, {"commandName": "Enable Snap to Pixels", "toggle": true});
}

document.getElementById("btnEnableSnapToPixels").addEventListener("click", btnEnableSnapToPixels_Click);
document.getElementById("btnDisableSnapToPixels").addEventListener("click", btnDisableSnapToPixels_Click);

and also:

const {executeAsModal} = require("photoshop").core;
const {batchPlay} = require("photoshop").action;
const app = require('photoshop').app

async function toggleSnapToPixels(toggle) {
   const result = await batchPlay(
      [
         {
            _obj: "set",
            _target: [
               {
                  _ref: "property",
                  _property: "generalPreferences"
               },
               {
                  _ref: "application",
                  _enum: "ordinal",
                  _value: "targetEnum"
               }
            ],
            to: {
               _obj: "generalPreferences",
               transformsSnapToPixels: toggle,
               legacyPathDrag: true
            },
            _options: {
               dialogOptions: "dontDisplay"
            }
         }
      ],
      {
         synchronousExecution: false,
         modalBehavior: "execute"
      }
   );
}

async function btnDisableSnapToPixels_Click() {
   await executeAsModal(toggleSnapToPixels(false), {"commandName": "Disable Snap to Pixels"});
}

async function btnEnableSnapToPixels_Click() {
   await executeAsModal(toggleSnapToPixels(true), {"commandName": "Enable Snap to Pixels"});
}

document.getElementById("btnEnableSnapToPixels").addEventListener("click", btnEnableSnapToPixels_Click);
document.getElementById("btnDisableSnapToPixels").addEventListener("click", btnDisableSnapToPixels_Click);

First code block gives me an error. Second fails silently. I’ve only been able to get it to work by defining two functions: one for enabling, and another for disabling. The redundant code is driving me nuts.

when using executeAsModal you should use an object called descriptor to pass command arguments.

async function transformsToogleSnapToPixels(executionContext, toggle) {
   const result = await batchPlay(
      [
         {
            _obj: "set",
            _target: [
               {
                  _ref: "property",
                  _property: "generalPreferences"
               },
               {
                  _ref: "application",
                  _enum: "ordinal",
                  _value: "targetEnum"
               }
            ],
            to: {
               _obj: "generalPreferences",
               transformsSnapToPixels: toggle,
               legacyPathDrag: true
            }
         }
      ],{}
   );
}

async function btnDisableSnapToPixels_Click() {
   await executeAsModal(transformsToogleSnapToPixels, {"commandName": "Disable Snap to Pixels", "descriptor": false});
}

async function btnEnableSnapToPixels_Click() {
   await executeAsModal(transformsToogleSnapToPixels, {"commandName": "Enable Snap to Pixels", "descriptor": true});
}

please report back with the result or any issues

Still not working. The code above fails silently.

Here’s my full code, ignore the irrelevant functions.

const {executeAsModal} = require("photoshop").core;
const {batchPlay} = require("photoshop").action;
const app = require('photoshop').app

async function toggleSnapToPixels(executionContext, toggle) {
   const result = await batchPlay(
      [
         {
            _obj: "set",
            _target: [
               {
                  _ref: "property",
                  _property: "generalPreferences"
               },
               {
                  _ref: "application",
                  _enum: "ordinal",
                  _value: "targetEnum"
               }
            ],
            to: {
               _obj: "generalPreferences",
               transformsSnapToPixels: toggle
            }
         }
      ],{}
   );
}

async function showExtras(state){
   let viewLocalString = core.translateUIString("$$$/Menu/View");
   let extrasLocalString = core.translateUIString("$$$/Menu/View/Extras");
   let extrasIsChecked = (await batchPlay([{
       "_obj": "get",
       "_target": [{"_property": "menuBarInfo"},{"_ref": "application", "_enum": "ordinal", "_value": "targetEnum"},]
   }], {}))[0].menuBarInfo.submenu.find( m => m.name == viewLocalString ).submenu.find( m => m.name == extrasLocalString).checked
   if (extrasIsChecked != state) {
       await core.performMenuCommand({commandID: 3500})
   }
}

async function openNeuralFilters() {
   const result = await batchPlay(
      [
         {
            _obj: "neuralGalleryFilters",
            _options: {
               dialogOptions: "display"
            }
         }
      ],
      {
         synchronousExecution: false,
         modalBehavior: "wait"
      }
   );
}

async function btnDisableSnapToPixels_Click() {
   await executeAsModal(toggleSnapToPixels, {"commandName": "Disable Snap to Pixels", "descriptor": false});
}

async function btnEnableSnapToPixels_Click() {
   await executeAsModal(toggleSnapToPixels(true), {"commandName": "Enable Snap to Pixels", "descriptor": true});
}

async function btnDisableExtras_Click() {
   await executeAsModal(showExtras, {"commandName": "Disable Extras", "descriptor": false});
}

async function btnEnableExtras_Click() {
   await executeAsModal(showExtras, {"commandName": "Enable Extras", "descriptor": true});
}

async function btnOpenNeuralFilters_Click() {
   await executeAsModal(openNeuralFilters, {"commandName": "Open Neural Filters"});
}

document.getElementById("btnEnableSnapToPixels").addEventListener("click", btnEnableSnapToPixels_Click);
document.getElementById("btnDisableSnapToPixels").addEventListener("click", btnDisableSnapToPixels_Click);
document.getElementById("btnOpenNeuralFilters").addEventListener("click", btnOpenNeuralFilters_Click);
document.getElementById("btnDisableExtras").addEventListener("click", btnDisableExtras_Click);
document.getElementById("btnEnableExtras").addEventListener("click", btnEnableExtras_Click);

Just a screenshot of the plugin:

Woohoo. I got it to work after stealing some code from an Adobe sample plugin. Here’s what worked for me:

const {executeAsModal} = require("photoshop").core;
const {batchPlay} = require("photoshop").action;
const app = require('photoshop').app

async function toggleSnapToPixels(toggle) {

   const batchCommands = {
      _obj: "set",
      _target: [
         {
            _ref: "property",
            _property: "generalPreferences"
         },
         {
            _ref: "application",
            _enum: "ordinal",
            _value: "targetEnum"
         }
      ],
      to: {
         _obj: "generalPreferences",
         transformsSnapToPixels: toggle
      }
    };

    return await require("photoshop").core.executeAsModal(async () => {
      await require('photoshop').action.batchPlay([batchCommands], {});
    }, { commandName: "Enable or Disable Snap to Pixels"});

}

async function btnDisableSnapToPixels_Click() {
   const result = await toggleSnapToPixels(false);
}

async function btnEnableSnapToPixels_Click() {
   const result = await toggleSnapToPixels(true);
}

document.getElementById("btnEnableSnapToPixels").addEventListener("click", btnEnableSnapToPixels_Click);
document.getElementById("btnDisableSnapToPixels").addEventListener("click", btnDisableSnapToPixels_Click);

I’ve never got how to use the descriptor prop in the executeAsModal() option parameter myself. The working code is a common pattern though: stick an async anonymous function in there, and just use commandName in the options.

Some people like to build the batchPlay object literal first, although I tend to prefer it inline. Side note: you already have required batchPlay on top, no need to await the full require statement again.

it took me time to wrap my head around but I managed to use it successfully

I think it produces cleaner code.

you basically define a property in options called descriptor it could be an object/array/string/boolean/integer…

executeAsModal(targetFunction, {"commandName": "Disable Snap to Pixels", "descriptor": "any value, any type"});`

function targetFunction(execContext, myString){
    //myString (2nd parameter) always holds the descriptor.
    console.log(myString);
    // output: "any value, any type"
    // you can pass an object that holds multiple properties
}
2 Likes

I’ve just realised that the role of descriptor in executeAsModal() is exactly the same as the info object in action.recordAction():

await async require("photoshop").action.recordAction(
  { name, methodName},
  { /* ... */ } // <- info object
);

Just saying it out loud for the posterity :grin:

—Davide

1 Like