User is able to close a document while modal dialog is showing

The user is able to close a document by clicking the “x” button while a modal dialog is showing. Full disclosure: I am showing the dialog with dialog.uxpShowModal() because I like having the text in the title bar. ( PS 27.1.0 release, OSX Sonoma 14.4.1 )

Do you have that call inside an executeAsModal context?

Otherwise, try the (should be documented but probably isn’t yet) option:
{lockDocumentFocus: true}

3 Likes

I have also seen this ability to interact with PS while modal (clicking document tabs). Sometimes you have to click aggressively / repeatedly. Document switches are the cause of a large number of modal fails / race conditions I have seen over the year (less so these days as I have build a lot of defensive code).

That lockDocumentFocus sounds interesting. Is there a way to lock / unlock inside a modal if you want to add some protection but will also need to switch documents within that modal?

Which version was that added in, please?

Yes, the call is inside executeAsModal and yes lockDocumentFocus works! Thank you!

On a related note I feel like the user shouldn’t be able to pan the document with a trackpad either since the scrollbars are non responsive and zoom with trackpad is nonresponsive.

Speaking of documentation, I can’t find any adobe doc that mentions uxpShowModal() at all. Why is the current/future implementation showModal() with no options?

I would say that it should be usable all the way back to Ps 2022 (23.0). Apparently, it slipped through the cracks for documentation.

@samgannaway Could you add a brief description here of what it does and how to use it, or link to documentation if that’s being added now? Sounds like it could be useful for some issues I’ve had with document switching and rejection of PS menu commands (race conditions in the back end).

1 Like

From my experimentation: I use it like this and it prevents the user from being able to close the active document or switch active document. It also grays out a lot of menu items that should not be available during a modal dialog ( without it they are selectable but don’t do anything )

await dialog.uxpShowModal({
    title: "Dialog Title", 
    resize: "none",
    lockDocumentFocus: true
});

It is still strange that you can pan with a trackpad, but not the scrollbars while a modal dialog is showing. A native Photoshop modal dialog ( like IMAGE→ADJUST→THRESHOLD ) lets you pan with the scrollbars and zoom in and out with keyboard and with menu items ( but not the trackpad ) while it is showing.

Feature request: I would love to be able to zoom and pan the active document while my modal dialog is showing.

Ah, so this is only for the UXP dialogs? My issues are primarily race conditions unrelated to showing dialogs (they get exposed if you start switching the active document via script).

Your script switches the active document but the user has the ability to also switch it and you are reliant on it not changing out from under you? What is your script doing?

I don’t want the user to switch. I had misinterpreted what this command may do.

The main issue is that PS returns bad document info immediately after using script to switch active documents under mostly rare conditions (this lasts for microseconds, often not even ms - but can return null for app.activeDocument with open images). Running synchronous JS will not avoid this issues - it is a race condition on the back end. I’ve run various tests which confirm this (such as running a loop which tests for null and you’ll see the value switch for app.activeDocument while doing nothing inside executeAsModal).

It has gotten better over time and I’ve added a lot of defensive code, but these issues account for the majority of the bug reports I see these days (as they are the only ones I cannot fix on my end - and they are impossible to reproduce, often even on affected hardware).

Interesting. So sometimes, right after you switch the active document in your script, app.activeDocument is null? That sound very not fun to track down. Do you set the active document with await?

await batchPlay([{
    _obj: "select",
    _target: [{ _ref: "document", _id: doc.id }]
}], { synchronousExecution: true });

If you’re doing multi-document operations, I wonder if storing IDs at the start and referencing explicitly might be more reliable than trusting app.activeDocument throughout.

Correct. The use of app.activeDocument may return null for a moment. I suspect BatchPlay is returning a pointer to an address which has not yet populated as the back end is not truly synchronous, even if you write your JS that way.

The issue has been improved over time, but never truly fixed. Certain conditions can make it more likely: certain computers (it’s a timing thing), floating documents seem to be worse, certain PS batch features, etc.

app.activeDocument is just a wrapper for BatchPlay referencing doc IDs. The problem is in the back end and you cannot get around it - though you can do defensive things like wait, test for valid current IDs (sometimes the wrong one vs null is returned), etc.

Yes, my code uses await. It is extensively synchronous / tested in areas vulnerable to this issue. I’ve created a lot of defensive fixes after years of dealing with this issue. I have also written tests which confirm issue is in a backend race condition (I only change documents with a function I built to test and manage this issue). If your code involved document switches or listens to events after a document close/switch/new event, you may be effected with enough use of the code. I’ve got it down to the point where I only see a report of the issue maybe once a week - and it almost never repeats for that user. Impossible to reproduce.

Maybe this is helpful for someone. Here’s how I check if a document is valid. This method has worked quite well for me.

function isDocValid(docId) {
    return require(“photoshop”).action.validateReference({ _ref: “document”, _id: docId });
};
2 Likes

Thanks, Tom. That may be faster than the loop I was testing.

I just did a test and the user can easily close or change the active document while I am in the middle of a long calculation. This seems like a fundamental flaw in executeAsModal, am I missing something?

console.log("before", app.activeDocument);

const start = Date.now();
let counter = 0;
while (Date.now() - start < 5000) {
  counter++;
}
console.log(`Counted to ${counter}`);

console.log("after", app.activeDocument);

I am only able to close the document by clicking on its “X” button, not with the keyboard or the menu. I only seem to be able to start interacting once the non determinate progress meter shows up, before that the cursor is an animated waiting cursor. Clicks to close the active document are cached and execute as soon as the progress meter displays. I am even able to open native dialogs from the menu such as FILE→AUTOMATE→BATCH

If I bring up my own progress meter with await executionControl.reportProgress({ value: 0, commandName: "Modal Test" });the same thing happens.

Agreed. I believe you can change the active document as well. Some allowance for zooming / panning the active document makes sense while viewing modal dialogs (generally harmless and helpful for user review in dialogs). But any setting which truly alters the active document (or which document is active) seems like something which should never be allowed during executeAsModal, as that protocol should protect against any unsafe changes outside the current script execution.