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.

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.