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.
Jarda
May 2, 2023, 5:06pm
2
fmotion1:
modalBehavior: "wait"
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.
Maher
May 3, 2023, 1:23pm
4
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.
Maher
May 10, 2023, 4:20pm
8
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
—Davide
1 Like