How can I detect when ActiveDocument changed?

Is there a way to detect when ActiveDocument was changed to another document? E.g. if I have 2 files opened and I click from one active file to another, how can my UXP plugin detect the change?

I tried to guess and check for “changed” event, but that didn’t work. It only triggered once upon loading the plugin.

require("photoshop").app.activeDocument.addEventListener("changed", showAlert("changed"));

Thank you.

Yes indeed, you can use Event Listeners for that.

Here’s how to use event listening in UXP and Photoshop:
https://www.adobe.io/photoshop/uxp/ps_reference/media/advanced/event-listener/

Just for better understanding: The “changed” event does not exist, but this type of function usually takes a callback function as the second parameter. So you’d have to write () => showAlert("changed"), otherwise the code will be executed immediately upon load, no matter which event key you use.

The docs don’t make it super clear what the parameters of addNotificationListener are - it’s (eventKey, eventDescriptor), so you can evaluate the descriptor to prevent your code from executing when other select events fire (e.g. select layer).
A select document descriptor looks somewhat like this:

 {
      "_obj": "select",
      "_target": [
         {
            "_ref": "document",
            ...
         }
      ],
...
 }

So you can check for *descriptor*._target[0]._ref === 'document'

“select” event is not reliable. There are multiple ways to change active document.

You’re right. I was refering to the case:

If you want to be notified about any change to the active Document in general, there’s also close, open etc. to consider.

Correct, open and close do not fire any events (that I’ve seen) that you can hook into. Which is kinda nuts, honestly. If I switch between two open documents, the Select event fires, but if I open a document and my panel is already open there is zero notifications.

This is another sore spot for me with UXP because my panels have state based on whether or not the active layer is one that it made. If the user is using my panel and opens another document, the panel retains whatever state it had previously instead of resetting to the a ready state. There’s no way around this, that I’ve found…unless someone knows of an event I’ve somehow missed??? (…although reading this thread, it doesn’t appear like I have.)

@simonhenke are you saying that there are open and close events to hook into? Alchemist doesn’t show them firing. I have the following defined in my project and “select” is the only one that ever fires:

    require('photoshop').action.addNotificationListener([
        { event: "select" },
        { event: "open" },
        { event: "close" }
    ], this.managePanelState);

In terms of event listening in PS and UXP + Vanilla JS, here’s how I listen to pretty much anything I need im my panels:

var listener = (e, d) => {
  myEvent = e;
  myData = d;

  console.log(myEvent, myData); // just display whatever event PS is catching

  if ((myEvent == "select") || (myEvent == "make") || (myEvent == "open")) {
    console.log("app.activeDocument._id", app.activeDocument._id)
  }

  if (myEvent == "close") console.log("Bye !");
  if (myEvent == "open") console.log("Hello !");
}

action.addNotificationListener([{ // pick your event(s)
    event: "select"
  },
  {
    event: "open"
  },
  {
    event: "make"
  },
  {
    event: "move"
  },
  {
    event: "close"
  },
  {
    event: "delete"
  },
  {
    event: "show"
  },
  {
    event: "hide"
  },
  {
    event: "set"
  }
], listener);

Hop this helps.

3 Likes

open, make and close are firing in my Alchemist, so the listener should work, too :thinking:

Note: if the document will be changed by someone else script, then you might not get an event at all… with the code used above.

Then perhaps count the history steps in the current doc :thinking:

All history steps have unique ID during the whole PS app session. Anyway if you select a different layer it won’t go into history. And if you target the active layer then the script can assume a different layer than needed.

I ended up using this suggestion from DanielAllen and all three events fire reliably. Thank you everyone for your suggestions.

require('photoshop').action.addNotificationListener([
        { event: "select" },
        { event: "open" },
        { event: "close" }
    ], this.managePanelState);

What about duplicate document? Or edit smart object content?

Yep. One more that I found is the smartBrushWorkspace event, which can also create (and select) a new document.

Maybe one could also listen for the layersFiltered event, it often occurs after the target document switches - even when called from other plugins.

@simonhenke could you clarify that a bit more for me? I have a Brett vor the Kopf i think.
What is the descriptor and how do i bake some code from it?
Like so?!
if (myEvent == "select"._target[0]._ref === ‘document’) {...}

(Action-)Descriptor is just the name for an object that describes (hence the name) a certain event/operation that happened in Photoshop, including all necessary parameters and the target which will handle the operation (layer, channel, etc).
If you register an event listener for specific events (in this case the select event), Photoshop will call the handler function that you pass as a parameter, everytime a select event occurs. But select is not unique to just selecting a document, it will also fire when a layer or a channel gets selected.

That’s why you should check if the descriptor is targeted at an object of the document class. (You get the descriptor returned by the event as a parameter in your event handler, as described in this thread).

But as Jarda pointed out, select isn’t the only event altering the active document. That’s why I personally use the layersFiltered event, which seems to happen on every change to the active document (select, open, close, etc.)

1 Like

I personally use select+idle event. Idle does pooling e.g. each 500ms when PS has nothing to do. But in my case, I need it only for UI so when panel is closed (but active in background) then it still listens but does not execute any additional batchplay

1 Like

I think layersFiltered is a hack. It does not trigger when you close your last document. And maybe one day someone will decide e.g. not to emit an event when the layers panel is closed or when a filter is disabled and then you gonna update e.g. 5 of your plugins.

1 Like

Thanks for the explanation @simonhenke . I finally understood how that works.
I now use the layersFiltered event and it works very well for my purposes (doc change), although i’m a bit unsure because of what Jarda said about it. But i guess i’ll have to live with it until the development team comes up with a proper method.