Mini Kabibi Habibi
/*************************************************************************
* ADOBE CONFIDENTIAL
* ___________________
*
* Copyright 2024 Adobe
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Inc. (“Adobe”) and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe and its
* suppliers and are protected by all applicable intellectual property
* laws, including trade secret and copyright laws.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe.
**************************************************************************/
const psAction = window.require('photoshop').action;
const psCore = window.require('photoshop').core;
const hostActions = {};
async function showCoachMarkPs(command){
try {
await psAction.batchPlay([command], {}, function() { });
} catch (error) {
console.error('Error showing coachmark:', error);
}
}
hostActions.showCoachMark = async function(cmd) {
const target = {
_ref: 'application',
_enum: 'ordinal',
_value: 'targetEnum'
};
const command = {
_obj : 'showCoachmark',
_target : target,
type : 'text',
position : {
arrow : cmd.arrow ? cmd.arrow : 'top-right',
arrowOrientation : cmd.arrowOrientation,
type : 'absolute',
x : cmd.position.x,
y : cmd.position.y
},
description : cmd.message
};
// console.log('What Plugins Panel passes to batchplay:');
// console.log('arrowOrientation : ' + cmd.arrowOrientation);
// console.log('x: ' + cmd.position.x);
// console.log('y: ' + cmd.position.y);
await showCoachMarkPs(command);
}
async function isDocumentView() {
const target = { _ref: [{ _property: 'visible' }, { _ref: 'document', _enum: 'ordinal', _value: 'targetEnum' }] };
const result = await psAction.batchPlay([{ _obj: 'get', _target: target }], {});
return result && result.length >= 1 && result[0]['visible'];
}
async function isHomescreenVisible() {
const target = { _ref: [{ _property: 'homeScreenVisibility' }, { _ref: 'application' }] };
const visibility = await psAction.batchPlay([{ _obj: 'get', _target: target }], {});
return visibility && visibility.length >= 1 && visibility[0].homeScreenVisibility;
}
function listenToHomeScreenVisibility(homeScreenVisibilityChangedCallback) {
psAction.addNotificationListener([{event: 'homeScreenVisibilityChanged'}], homeScreenVisibilityChangedCallback);
}
function listenToAppVisibility(appVisibilityChangedCallback) {
psCore.addNotificationListener('OS', ['activationChanged'], appVisibilityChangedCallback);
}
hostActions.addUINotificationListener = function(events, handler) {
psCore.addNotificationListener('UI', events, handler);
}
async function isAppInForeground() {
const command = [
{
_obj: 'multiGet',
_target: {
_ref: 'application',
_enum: 'ordinal',
_value: 'targetEnum'
},
extendedReference: [['active']]
}
];
const appVisibility = await psAction.batchPlay(command, {});
return appVisibility && appVisibility.length >= 1 && appVisibility[0].active;
}
async function getApplicationFrameInfo() {
const command = {
_obj: 'owlAction',
_target: {
_ref: 'application',
_enum: 'ordinal',
_value: 'targetEnum'
},
owlCommand: 'getApplicationFrameInfo'
};
try {
const applicationFrameInfo = await psAction.batchPlay([command], {}, function() {});
return applicationFrameInfo[0].globalBounds;
} catch (error) {
console.error('Error getting application frame info:', error);
}
}
function getDisplayConfiguration() {
return psCore.getDisplayConfiguration({});
}
hostActions.hideCoachMark = async function() {
// [TODO] send unique Id as well
const command = {
_obj: 'hideCoachmark',
_target: {
_ref: 'application',
_enum: 'ordinal',
_value: 'targetEnum'
}
};
try {
await psAction.batchPlay([command], {}, function() { });
} catch(error) {
console.error('Error hiding coachmark:', error);
}
}
async function getIconBounds(command) {
try {
const globalIconBounds = await psAction.batchPlay([command], {modalBehavior: 'execute'});
const iconBounds = {
left : globalIconBounds[0].panelInfo[0].globalIconicBounds.left,
right : globalIconBounds[0].panelInfo[0].globalIconicBounds.right,
top : globalIconBounds[0].panelInfo[0].globalIconicBounds.top,
bottom : globalIconBounds[0].panelInfo[0].globalIconicBounds.bottom
};
return iconBounds;
} catch (error) {
console.error('Error getting the icon bounds:', error);
}
}
async function getPanelBounds(command) {
try {
const globalPanelBounds = await psAction.batchPlay([command], {modalBehavior: 'execute'});
const panelBounds = {
left : globalPanelBounds[0].panelInfo[0].globalBounds.left,
right : globalPanelBounds[0].panelInfo[0].globalBounds.right,
top : globalPanelBounds[0].panelInfo[0].globalBounds.top,
bottom : globalPanelBounds[0].panelInfo[0].globalBounds.bottom
};
return panelBounds;
} catch (error) {
console.error('Error getting the panel bounds:', error);
}
}
async function getElementBounds(element, id) {
const command = {
_obj: 'owlAction',
_target: { _ref: 'application' },
owlCommand: 'getPanelInfo',
owlPanelID: id
};
if (element == 'panel') {
return await getPanelBounds(command);
} else if (element == 'icon') {
return await getIconBounds(command);
} else {
console.error(element + ' is not a valid input');
}
}
async function getPluginsMenuBarBounds() {
try {
const mainMenuItems = await require('photoshop').core.getMainMenuInfo(1);
const bounds = mainMenuItems.children.find((child) => child['aria-label'] == 'Plugins').globalBounds;
return bounds;
} catch (error) {
console.error('Error obtaining main menu info:', error);
}
}
hostActions.getGlobalBounds = async function(type, id) {
let bounds;
if (type == 'pluginIcon') {
bounds = await getElementBounds('icon', id);
} else if (type == 'hostApp') {
bounds = await getApplicationFrameInfo();
} else if (type == 'appmenu') {
bounds = await getPluginsMenuBarBounds();
} else if (type == 'pluginsPanel') {
const isMac = require('os').platform() === 'darwin';
if (isMac) {
// [TODO] : Currently, the Browse Plugins coachmark can appear in the wrong
// location on windows only. Temporarily disabling until we get a fix. If
// win, this will return undefined, which plugins panel will return early.
bounds = await getElementBounds('panel', id);
}
} else {
console.error(type + ' is not a correct global bounds type');
}
// console.log('What we pass to Plugins Panel');
// console.log('bounds: {left: ' + bounds.left + ', right: ' + bounds.right + ', top: ' + bounds.top + ', bottom: ' + bounds.bottom + '}');
return bounds;
}
hostActions.isVisible = function(viewType) {
switch (viewType) {
case 'document': {
return isDocumentView();
}
case 'homeScreen': {
return isHomescreenVisible();
}
case 'appInForeground': {
return isAppInForeground();
}
default:
return new Promise(resolve => {resolve (false);});
}
}
hostActions.addListenerToHostEvents = function(eventStr, callback) {
switch (eventStr) {
case 'homeScreenVisibility': {
try {
listenToHomeScreenVisibility((event, description) => {callback(description.visible);});
} catch (error) {
console.error('Error registering for home screen visibility', error);
}
break;
}
case 'appVisibility': {
try {
listenToAppVisibility(async (event, description)=> {callback(description.active);});
} catch (error) {
console.error('Error registering for app visibility', error);
}
break;
}
default:
console.error('Error: registering for unknown event');
break;
}
}
// [TODO] : check if HostApps return scaler as 1 for Mac.
// If not, then this API will be required. Also put this in platform specific file.
function getScaleFactorFromScreen(screen /*IDisplayConfig*/) {
const isMac = require('os').platform() === 'darwin';
if (isMac || !screen) {
// Scale factor on MAC is always 1.
return 1;
}
return screen.scaleFactor;
}
async function getScreens() /*: Promise<IDisplayConfig[]> */ {
return await getDisplayConfiguration();
}
function scaleWithScaleFactor(position, factor) {
const result = {};
for (const key in position) {
result[key] = Math.floor(position[key] * factor);
}
return result;
}
/**
* Given available screens, determine which screen the specified point (targetX, targetY)
*/
function getOwnerScreen(screens /*IDisplayConfig[]*/, targetX /*number*/, targetY /*number*/) /*: false | IDisplayConfig*/ {
const screen = screens.find(screen => {
const scaleFactor = getScaleFactorFromScreen(screen);
const globalBounds = scaleWithScaleFactor(screen.globalBounds, 1/scaleFactor) /*as IBounds*/;
return targetX >= globalBounds.left &&
targetX <= globalBounds.right &&
targetY >= globalBounds.top &&
targetY <= globalBounds.bottom;
});
if (!screen) {
return false;
}
return screen;
}
async function getScalerFactorPs() {
const appFrameBounds = await hostActions.getGlobalBounds('hostApp', '');
let top = 0;
let left = 0;
const screens = await getScreens();
let ownerScreen;
// Try the bottom mid position of the app frame
top = Math.floor(appFrameBounds.bottom);
left = Math.floor((appFrameBounds.right + appFrameBounds.left)/2);
ownerScreen = getOwnerScreen(screens, left, top);
// Try the left corner of the app frame
if (ownerScreen == false) {
// Try the left corner of the app frame
ownerScreen = getOwnerScreen(screens, appFrameBounds.left, appFrameBounds.top);
if (ownerScreen == false) {
// Try the right corner of the app frame
ownerScreen = getOwnerScreen(screens, appFrameBounds.right, appFrameBounds.top);
if (ownerScreen == false) {
return;
}
}
}
let scalerFactor = getScaleFactorFromScreen(ownerScreen);
return new Promise(resolve => {resolve (scalerFactor);});
}
hostActions.getScalerFactor = async function() {
return await getScalerFactorPs();
}
hostActions;