Copy link to clipboard
Copied
Hi,
I'm currently working on a project that mixes several needs.
On one hand, I have to pop up a ScriptUI window (non-modal palette), using the BridgeTalk message technique, as outlined here (in order to keep the palette open, waiting for user interaction).
On the other hand, I must build a recordable script (as this tutorial shows).
And finally the History palette has to record one step only - suspendHistory() is needed.
The script:
1. Set some Smart Objects
2. Calls a function that pops up the non-modal Window
3. Then the user interact with GUI elements (sliders, etc) that in turn trigger some extra processing (layers on/off, smart filters, etc).
4. Eventually the user click the Apply/Cancel button, some layer flattening occur, and the Window closes.
I have successfully implemented the above scenario using a Window palette with basically a while-true loop calling waitForRedraw(), but I'm in need to switch to BridgeTalk because, on PCs, the waitForRedraw() seem to keep alive the Window, but doesn't make it truly non-modal (on Mac it works, I've written about it here).
All that said, my problem is, finally, that even though I wrap the Window drawing function into a suspendHistory() statement, being BridgeTalk asynchronous, everything that happens at #3 (user interacting with Window's GUI, triggering layers operations), escapes the suspendHistory step, and makes the History palette dirty - this happens, as far as I understand, because the JS engine doesn't keep itself busy with the Window (as the waitForRedraw() would have done) but goes along instead.
Question: is there any workaround in order to enclose everything in a single history step (or make BridgeTalk synchronous - if it would ever work)?
Thanks in advance!
Davide
Copy link to clipboard
Copied
I've cooked up a very simplified example just to show the dynamic involved:
// EasyWin.jsx
var win, windowResource;
var dup = app.activeDocument.activeLayer.duplicate();
dup.name ="DUP";
var myWin = function() {
windowResource = "dialog { orientation: 'column', alignChildren: ['fill', 'top'], preferredSize:[300, 130], text: 'ScriptUI Window - palette', margins:15, sliderPanel: Panel { orientation: 'row', alignChildren: 'right', margins:15, text: ' PANEL ', st: StaticText { text: 'Value:' }, sl: Slider { minvalue: 1, maxvalue: 100, value: 30, size:[220,20] }, } bottomGroup: Group{ cd: Checkbox { text:'Checkbox value', value: true }, cancelButton: Button { text: 'Cancel', properties:{name:'cancel'}, size: [120,24], alignment:['right', 'center'] }, applyButton: Button { text: 'Apply', properties:{name:'ok'}, size: [120,24], alignment:['right', 'center'] }, }}";
win = new Window(windowResource);
win.sliderPanel.sl.onChanging = function() {
this.parent.st.text = Math.floor(this.value);
dup.opacity = this.parent.st.text;
}
win.bottomGroup.cancelButton.onClick = function() {
return win.close();
};
win.bottomGroup.applyButton.onClick = function() {
return win.close();
};
this.run = function() {win.show()}
}
var MyWin = new myWin();
app.activeDocument.suspendHistory('SINGLE STEP','MyWin.run();');
If you run the above code in PS (open at least an image before), a simple Dialog Window will pop up; a copy of the background layer is created and the slider controls the duplicate layer's opacity. Not cool but serves his purpose.
Everything is packed into a single History step - i.e. the history state is written *after* the Apply button click.
Now, try to modify just "dialog" with "palette" and the window will just flashes - it's a known behavior.
// EasyWinCallee.jsx
var win, windowResource;
var dup = app.activeDocument.activeLayer.duplicate();
dup.name ="DUP";
var myWin = function() {
windowResource = "palette { orientation: 'column', alignChildren: ['fill', 'top'], preferredSize:[300, 130], text: 'ScriptUI Window - palette', margins:15, sliderPanel: Panel { orientation: 'row', alignChildren: 'right', margins:15, text: ' PANEL ', st: StaticText { text: 'Value:' }, sl: Slider { minvalue: 1, maxvalue: 100, value: 30, size:[220,20] }, } bottomGroup: Group{ cd: Checkbox { text:'Checkbox value', value: true }, cancelButton: Button { text: 'Cancel', properties:{name:'cancel'}, size: [120,24], alignment:['right', 'center'] }, applyButton: Button { text: 'Apply', properties:{name:'ok'}, size: [120,24], alignment:['right', 'center'] }, }}";
win = new Window(windowResource);
win.sliderPanel.sl.onChanging = function() {
this.parent.st.text = Math.floor(this.value);
dup.opacity = this.parent.st.text;
}
win.bottomGroup.cancelButton.onClick = function() {
return win.close();
};
win.bottomGroup.applyButton.onClick = function() {
return win.close();
};
this.run = function() {win.show()}
}
var MyWin = new myWin();
app.activeDocument.suspendHistory('SINGLE STEP','MyWin.run();');
I could use WaitForRedraw() but as I've written before, I can't.
Now, save that file somewhere naming it EasyWinCallee.jsx and, besides it, save another file with this code (saving them is mandatory in order for the following script to read the previous one):
// BTCaller.jsx
#target photoshop
var myPath = File($.fileName).parent;
var callee = new File(myPath + "/EasyWinCallee.jsx");
if (!callee.exists) {alert("Missing!")}
callee.open('r');
var message = callee.read();
callee.close();
var bt = new BridgeTalk();
bt.target = "photoshop";
bt.body = message;
bt.send();
Run BTCaller.
The Palette Window shows and sticks there, but as soon as it's shown, the history status is written and each time you drag the slider a new step is added to the History Palette. Which is what I would like to avoid...
Any suggestion?
Davide
Copy link to clipboard
Copied
This is the best I have come up with so far. It doesn't suspend history but at least it limits all the slider change while the control has focus to one history state.
// EasyWinCallee.jsx
var win, windowResource;
var controlHistory = false;
var doc = app.activeDocument;
var dup = doc.activeLayer.duplicate();
app.activeDocument.activeLayer = dup;
dup.invert();
dup.name ="DUP";
var myWin = function() {
windowResource = "palette { orientation: 'column', alignChildren: ['fill', 'top'], preferredSize:[300, 130], text: 'ScriptUI Window - palette', margins:15, sliderPanel: Panel { orientation: 'row', alignChildren: 'right', margins:15, text: ' PANEL ', st: StaticText { text: 'Value:' }, sl: Slider { minvalue: 1, maxvalue: 100, value: 30, size:[220,20] }, } bottomGroup: Group{ cd: Checkbox { text:'Checkbox value', value: true }, cancelButton: Button { text: 'Cancel', properties:{name:'cancel'}, size: [120,24], alignment:['right', 'center'] }, applyButton: Button { text: 'Apply', properties:{name:'ok'}, size: [120,24], alignment:['right', 'center'] }, }}";
win = new Window(windowResource);
win.sliderPanel.sl.onChange = function() {
this.parent.st.text = Math.floor(this.value);
if(controlHistory){
doc.activeHistoryState = doc.historyStates[doc.historyStates.length-2];
}else{
controlHistory = true;
}
dup.opacity = this.parent.st.text;
}
win.sliderPanel.sl.addEventListener('blur', BlurHandler);
win.bottomGroup.cancelButton.onClick = function() {
return win.close();
};
win.bottomGroup.applyButton.onClick = function() {
return win.close();
};
this.run = function() {win.show()}
}
function BlurHandler(){
controlHistory = false;
}
var MyWin = new myWin();
app.activeDocument.suspendHistory('SINGLE STEP','MyWin.run();');
Copy link to clipboard
Copied
Thanks Mike, I appreciate you've taken some time to help!
I'm not sure I could implement this one (into a recordable script - shouldn't everything go into a single, "standard" history status?) but it looks very like the only compromise. I'll let you know if I come up with something different.
Thanks again!
Davide
Find more inspiration, events, and resources on the new Adobe Community
Explore Now