【CEP专题】 隐藏面板的妙用

【CEP专题】 隐藏面板的妙用

大家好,我又回来了。我已经考完试了,下一步就是等出成绩,等录取通知书啦。

接下来我会继续回归系列教程的更新,以专题的形式补充一些我之前没有讲到的内容。同时预计春节后,会开始写UXP部分的教程,敬请期待。

之前有小伙伴反馈文章的形式对一些操作性的内容不太友好,我接下来会试着同时发布文章和视频的方式,视频会发布到B站和知乎,欢迎大家点赞关注转发。这篇文章的视频链接在这里【CEP教程】隐藏面板的妙用】,视频和文章的侧重点不一样,建议结合来观看。

这篇专题文章给大家介绍CEP插件中一种独特的面板形态 - 隐藏面板,以及它可以用来完成的一些有趣的功能。在某些情况下对我们非常有帮助。

隐藏面板

从官方的文档里头,我们可以看到,CEP提供了4种面板形态:

  • 常规面板(Panel)
  • 模态面板(ModalDialog)
  • 非模态面板(Modeless)
  • 自定义面板(Custom)

我们通常用的就是常规面板,它的特点是可以自由拖动和调整大小,并且可以在菜单栏中找到入口并打开,同时面板的开启并不会影响到用户的其他操作。而模态面板类似一个对话框,它会强制阻断用户的操作,必须关闭此面板,用户才能继续操作其他内容。

Extension Types

今天我们要介绍的隐藏面板,其实就是自定义面板,它在整个Ps的运行周期中均不可见。那你可能就会想了,面板不可见,那我也打不开它,那能拿来干啥呢?我们反过来思考:既然用户无法打开它,也意味着用户关不了它。不像常规面板,如果用户关掉了面板,你的代码也就再没有执行的机会了。于是,隐藏面板因为用户无法进行关闭,这就给了我们一个永久运行生命周期的机会。看不见的隐藏面板意味着:

  1. 始终在后台运行
  2. 用户无感知

比如你希望在用户打开Ps的时候,就开始自动执行一些任务,或者你希望在用户整个Ps的运行时间内都可以随时运行你的代码,那这就是隐藏面板带来的具体价值所在。

我们下来介绍如何来创建一个隐藏面板

隐藏面板的创建

通过设置manifest.xml文件,我们可以创建一个隐藏面板。

  1. 设置manifest的版本为5.0或者更高
1
2
3
<ExtensionManifest Version="5.0">
...
</ExtensionManifest>
  1. 在Extension - UI - Type中设置为Custom
1
2
3
4
5
6
<Extension Id="com.sample.id">
...
<UI>
<Type>Custom</Type>
</UI>
</Extension>
  1. 在Extension - Lifecycle - AutoVisible中设置为false
1
2
3
4
5
6
<Extension Id="com.sample.id">
...
<Lifecycle>
<AutoVisible>false</AutoVisible>
</Lifecycle>
</Extension>
  1. 记得不要添加Menu这个标签,不然它会出现在菜单栏里头
  2. 在Extension - Lifecycle - StartOn中添加面板的启动事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<Extension Id="com.sample.id">
...
<Lifecycle>
<AutoVisible>false</AutoVisible>
<StartOn>
<!-- Photoshop dispatches this event on startup -->
<Event>applicationActivate</Event>
<!-- Premiere Pro dispatches this event on startup -->
<Event>com.adobe.csxs.events.ApplicationActivate</Event>
<!-- PS on Windows isn't consistently firing the above events on launch.
To ensure that we're launched, also start on the following -->
<Event>com.adobe.csxs.events.AppOnline</Event>
<Event>documentAfterActivate</Event>
</StartOn>
</Lifecycle>
</Extension>

其中最重要的,其实是第5步,既然面板是隐藏的,切没有入口,那它合适启动呢?这就是第5步要做的内容,它定义了一些宿主的事件,当宿主发生这些事件的时候,就会触发启动面板。比如上面的例子,当用户打开Ps的时候,就会触发启动面板。这就使得我们可以实现一个全生命周期的面板。

整个manifest.xml的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ExtensionManifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ExtensionBundleId="IamInvisible" ExtensionBundleVersion="1.0" Version="5.0">
<ExtensionList>
<Extension Id="IamInvisible" Version="1.0"/>
</ExtensionList>
<ExecutionEnvironment>
<HostList>
<Host Name="PHXS" Version="13.0"/>
</HostList>
<LocaleList>
<Locale Code="All"/>
</LocaleList>
<RequiredRuntimeList>
<RequiredRuntime Name="CSXS" Version="5.0"/>
</RequiredRuntimeList>
</ExecutionEnvironment>
<DispatchInfoList>
<Extension Id="IamInvisible">
<DispatchInfo>
<Resources>
<MainPath>./html/invisible.html</MainPath>
<CEFCommandLine>
<Parameter>--enable-nodejs</Parameter>
</CEFCommandLine>
</Resources>
<Lifecycle>
<AutoVisible>false</AutoVisible>
<StartOn>
<!-- Photoshop dispatches this event on startup -->
<Event>applicationActivate</Event>
<!-- Premiere Pro dispatches this event on startup -->
<Event>com.adobe.csxs.events.ApplicationActivate</Event>
<!-- PS on Windows isn't consistently firing the above events on launch.
To ensure that we're launched, also start on the following -->
<Event>com.adobe.csxs.events.AppOnline</Event>
<Event>documentAfterActivate</Event>
</StartOn>
</Lifecycle>
<UI>
<Type>Custom</Type>
<Geometry>
<Size>
<Height>1</Height>
<Width>1</Width>
</Size>
</Geometry>
</UI>
</DispatchInfo>
</Extension>
</DispatchInfoList>
</ExtensionManifest>

面板的功能

完成manifest.xml的设置之后,我们就可以开始写面板的功能了。面板的其他结构和常规面板是一致的。我们在html文件里头简单写一段代码来验证它是不是能够在Ps启动的时候就执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Invisible Panel</title>
<meta name="viewport" content="width=device-width,initial-scale=1.0">
</head>
<body>
<script src="../js/CSInterface-6.1.0.js"></script>
<script>
alert("这个弹窗来自隐藏面板");
</script>
</body>
</html>

这个时候,我们打开Ps,就会发现,这个弹窗已经出现了。这就说明我们的面板已经成功的启动了。并且你还无法在菜单栏里头找到它。

Invisible Panel

上面我们测试的是Js部分的执行,接下来看看JSX应该如何来使用。有一个地方需要注意的是,隐藏面板不支持ScriptPath 标签,即无法默认加载一个jsx脚本,不过我们可以通过Js去动态加载我们的jsx脚本来执行。

1
2
3
4
5
// html
var csInterface = new CSInterface();
var extensionRoot = csInterface.getSystemPath(SystemPath.EXTENSION);
var jsxFile = extensionRoot + "/jsx/main.jsx";
csInterface.evalScript(`$.evalFile('${jsxFile}')`);

这里的实现和我们常规面板开发是基本一致的。

隐藏面板的应用场景

上面我们已经完成了隐藏面板的开发,接下来我们来看看隐藏面板的应用场景。

  1. App启动时执行脚本

这个上面的示例已经介绍了,因为隐藏面板就是在App启动的时候开启的,我们在里面的代码就能够实现app自动时执行。

  1. 与常规面板交互

单独一个隐藏面板对用于而言是没有什么价值的,我们一般会提供一个常规面板,这个时候隐藏面板可以作为一个后台运行的服务,为我们的常规面板提供能力,比如说面板重启功能。

我们的插件发布的时候,一般会将面板设置为Persistent模式,这样用户每次打开能够停留在上一次关闭的状态,但是这样也会带来一个问题,就是如果我们的插件出现了问题,用户无法通过关闭面板来重启插件,这个时候我们就可以通过隐藏面板来实现这个功能。

插件重启流程

在前面的文章中,我们介绍过插件之间可以通过事件来进行通信,由于隐藏面板是常驻运行的,所以我们可以随时给它发送消息,让它去重启我们的面板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 主面板给隐藏面板发送消息
const csInterface = new CSInterface();
const extensionId = csInterface.getExtensionID();

const event = new CSEvent();
event.type = "my.restart.event"; // 这个是自定定义的一个命令
event.extensionId = extensionId;
event.scope = "APPLICATION";
// 传递的消息数据,我们设置了一个延迟时间
event.data = JSON.stringify({restart: 1000, extensionId: extensionId})
csInterface.dispatchEvent(event);
// 发送完消息后,延迟会,关闭当前插件面板
setTimeout(() => {
csInterface.closeExtension();
}, 100);

隐藏面板可以通过监听这个事件来接收到消息,然后去重启我们的面板。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 隐藏面板接收到消息后,重启面板
const cs = new CSInterface();
cs.addEventListener("my.restart.event", function(event) {
console.log("restart extension: " + JSON.stringify(event.data));
const data = event.data;
const extensionId = data.extensionId;
const time = data.restart;
setTimeout(function () {
// 重新打开面板
cs.requestOpenExtension(extensionId)
setTimeout(function () { cs.requestOpenExtension(extensionId) }, 1500)
}, time);
});

这里需要注意的是,如果你的插件已经设置了Persistent,需要先进行UnPersistent操作,避免重新打开的插件依然是之前的状态。

1
2
3
4
5
function unPersistent() {
const event = new CSEvent("com.adobe.PhotoshopUnPersistent", "APPLICATION");
event.extensionId = extensionId;
csInterface.dispatchEvent(event);
}
  1. 常驻后台服务

由于隐藏面板的常驻生命周期特性,我们可以通过它来实现一个常驻后台的服务,比如长链接与我们的服务器进行通信,这样你可以通过远端实时推送一些消息给你的用户。或者是一些数据的统计和上报等等,都可以在后台进行默默的操作。这方面涉及到的案例代码量都比较大,我就不在这里展示了,大家可以根据自己的需要去实现。

总结

这个是示例项目的代码地址,大家可以下载下来,自己去尝试一下。

以上,就是我们要介绍的关于隐藏面板的使用,这种纯应用型的知识是无法在官方文档里头找到的,也是许多前人实践的一些经验,希望能够帮助到大家。

另外需要注意的是,不同的宿主App(如Ps/ID/AI)等对隐藏面板的支持程度和特性都不太相同,大家使用的时候要特别小心,明白走进死胡同。

【CEP专题】 隐藏面板的妙用

https://uiscripting.com/2022/12/31/hidden-panel/

作者

小强

发布于

2022-12-31

更新于

2023-03-07

许可协议

评论