Skip to main content
Inspiring
April 7, 2025
Answered

AutocloseAlert, CustomConfirm, and CustomPrompt Messages

  • April 7, 2025
  • 1 reply
  • 2826 views

I found this thread: https://community.adobe.com/t5/illustrator-discussions/auto-close-alert-message/td-p/9398258

Images here: https://github.com/Marshall-Brooks/Sandbox/issues/2

 

See also:

https://community.adobe.com/t5/photoshop-ecosystem-discussions/simulate-button-press-in-script-ui/m-p/7460138

 

I'm somewhat spoiled by the Enhanced Message Box add-on for Microsoft Access which shows a standard Msgbox with a countdown of seconds until the box closes.

 

I used @Peter Kahrel's Scipt UI guide to modify the first linked script as follows:

function AutocloseAlert(message, delaySeconds, title){
    title = title || 'Alert';
    var alertWindow = new Window('palette', title);
//    var control_text = alertWindow.add('edittext', [0, 0, 500, 200], message, {multiline: true});
//  var control_text = alertWindow.add('edittext', , message, {multiline: true});   
    var control_text = alertWindow.add('edittext', undefined, message, {multline:true})
//    if(delaySeconds === 0){
        var control_close = alertWindow.add('button', undefined, 'OK');       
        control_close.onClick = function(){
            if(alertWindow){
                alertWindow.hide();
                alertWindow = null;
            }
        };
 // }

    alertWindow.show();
    alertWindow.update();
   
    if(delaySeconds > 0){
        $.sleep(delaySeconds * 1000);
        alertWindow.hide();
        alertWindow = null;
    }  
}

I'm having the following issues:

  • I don't like that the window shows grayed out (disabled) - presumably b/c of the sleep function. I'd prefer it to look like the zero second but disappear when I clicked OK or when the timer expired. I tried commenting out the line "$.Sleep(DelaySeconds * 1000) and replacing it with: 
        var startTime = new Date().getTime();
        while ( new Date().getTime() - startTime < delaySeconds * 1000 ) {alertWindow.update()} ​

But it didn't change how it worked - i.e. the window was still grayed out, and clicking the OK button did not close the window, but it did disappear after the delaySeconds interval.

  • I would prefer to have the OK button show the seconds remaining, but I think I need two timers for this and I'm not sure how to implement them.
  • Probably easiest - I'd prefer to have a minimum size for the window and a maximum before it wraps to two lines, but I can basically set my "message" text to achieve this. (An alert that is more than one line is probably too long anyway).

Thank you in advance!!!

Correct answer Marshall_Brooks

I think I see the problem, but not the solution.

"answer" is being returned by the .onClick of either button, but nothing is being returned by CustomPrompt(), but I don't see how to pass "answer" to CustomPrompt() so that it can then be passed to the calling function.

 

Solved - I'll update the code above in a minute ...


CustomPrompt Code:

var result;
result = CustomPrompt("test","input","title");
alert (result);
result = prompt("test","input","title");
alert (result);

function CustomPrompt(message, defaultinput, title)
{
    var usercancelled = false;
    title = title || "Script Alert";
    defaultinput = defaultinput || "";
    var win = new Window("dialog", title, undefined, {closeButton:false});
    win.orientation = "row"; // not deprecated and required here, despite what VS Code says.
    win.alignChildren = "top";
    win.margins = 10;

    var myInputGroup = win.add ("group");
    myInputGroup.orientation = "column";
    myInputGroup.alignChildren = "left";
    // Dialog Width To Be Defined:
    var w_width = 275;

    //  150 below is somewhat arbitrary. Run the script and see where the first line ends. Adjust w_static text to a bit longer than that. Uncomment the line below to get the text length,
    // Use that in the "if" statement.
//    alert (message.length)
    if (message.length < 150 && message.indexOf("\r")===-1 && message.indexOf("\n")===-1)
    {
    // single line
         var msg = myInputGroup.add('statictext', undefined, message);
    }
    else
    
    {
    // multiline
        var msg = myInputGroup.add('statictext', undefined, 'X', {multiline: true});
        // if not using standard font size, Font must be defined below, even if using set_font, or the message will truncate unpredictably.
        msg.graphics.font="Tahoma:14";

        var X_width = msg.preferredSize[0];
        msg.preferredSize = [-1,-1];
        msg.characters = ~~(w_width/X_width);
        msg.preferredSize[1] = -1;
        msg.text = message;
    }
    var InputBox = myInputGroup.add ("edittext", undefined, defaultinput);
    InputBox.characters = 30;
    InputBox.active = true;
    var myButtonGroup = win.add ("group");
    myButtonGroup.orientation = "column";
    var btnOk = myButtonGroup.add ("button", undefined, "OK", {name: "ok"});
    var btnCancel = myButtonGroup.add ("button", undefined, "Cancel", {name: "cancel"});
    btnOk.onClick = function ()
    {
        win.close();
    }
    btnCancel.onClick = function ()
    {
        win.close();
        usercancelled = true;
    }
    set_font (win, "Tahoma:14");
    win.show ();
    if (usercancelled ===true)
    {
        return null;
    }
    else
    {
        return InputBox.text
    }
} // end CustomPrompt

function set_font (control, font)
{
    for (var i = 0; i < control.children.length; i++)
    {
        if ("GroupPanel".indexOf (control.children[i].constructor.name) > -1)
        {
            set_font (control.children[i], font);
        }
        else
        {
        control.children[i].graphics.font = font;
        }
    }
}//--end set_font

Minor edits above. I had "if message.length < 15" instead of "if message.length < 150". Also I changed w_width to 275. Script was truncating after 6 lines of text with 250, but I don't know why and I am not sure why 275 works better.

1 reply

Community Expert
April 7, 2025

Try using Idle Task instead of sleep 

 

function AutocloseAlert(message, delaySeconds, title) {
    title = title || 'Alert';
    delaySeconds = delaySeconds || 5;

    var alertWindow = new Window('palette', title);
    alertWindow.orientation = 'column';
    alertWindow.alignChildren = 'fill';

    var textBox = alertWindow.add('statictext', undefined, message, { multiline: true });
    textBox.characters = 50;

    var button = alertWindow.add('button', undefined, 'OK (' + delaySeconds + ')');

    var countdown = delaySeconds;
    var idleTask;

    // Button closes early
    button.onClick = function () {
        if (idleTask && idleTask.isValid) {
            idleTask.remove();
        }
        alertWindow.close();
    };

    // This function will be called repeatedly by IdleTask
    function onIdleEvent() {
        countdown--;

        if (!button || !button.text) return;

        if (countdown <= 0) {
            if (idleTask && idleTask.isValid) {
                idleTask.remove();
            }
            alertWindow.close();
        } else {
            button.text = 'OK (' + countdown + ')';
        }
    }

    // Create IdleTask
    idleTask = app.idleTasks.add({ name: "AutocloseAlertTimer", sleep: 1000 });

    // Attach event listener
    idleTask.addEventListener(IdleEvent.ON_IDLE, function () {
        onIdleEvent();
    });

    alertWindow.show();
}
Inspiring
April 7, 2025

@Eugene Tyson - I didn't understand your code, although it looks really good, but I am getting an error "Undefined is not an object" on the line idleTask = app.itleTasks.add({name:"AutocloseAlertTimer", sleep: 1000})

I should have added - my script is being run in FrameMaker, but I was told the InDesign forum had better ScriptUI support.

Inspiring
August 14, 2025

Updated the scripts to auto-size and or wrap correctly with the help of this thread: https://community.adobe.com/t5/indesign-discussions/script-ui-dynamically-autosizing-statictext/td-p/15275567#M621971 

 

EDIT: Had to make a minor change to the "if" statement in AutocloseAlert(). The previous version would fail (only display the first line) if message length was < 80 and you have manual line feeds, e.g.:

AutocloseAlert("Line 1\rLine2");

EDIT2: Had to correct the "if" statement, not found returns -1, not 0.

 

Here are the final (I hope) versions:

function AutocloseAlert(message, delaySeconds, title, bgcolor) {
    title = title || "Script Alert";
    delaySeconds = delaySeconds || 0
    bgcolor = bgcolor || "gray";

     if(delaySeconds===0){
            var win = new Window("dialog", title, undefined, {closeButton:false});
    }
    else{
        var win = new Window("palette", title, undefined, {closeButton:false});
    }
    win.margins = 10;
    win.orientation = "column";
    win.alignChildren = "center";
    if(bgcolor==="white"){
        win.graphics.backgroundColor = win.graphics.newBrush (win.graphics.BrushType.SOLID_COLOR, [1.0, 1.0, 1.0]);
    }
    // Dialog Width To Be Defined:
    var w_width = 450;  

    //  80 below is somewhat arbitrary. Run the script and see where the first line ends. Adjust w_static text to a bit longer than that. Uncomment the line below to get the text length, Use that in the "if" statement.
    //alert (message.length)
    if (message.length < 80 && message.indexOf("\r")===-1 && message.indexOf("\n")===-1) {
    // single line
         var msg = win.add('statictext', undefined, message);
    }
    else{
    // multiline    
        var msg = win.add('statictext', undefined, 'X', {multiline: true});
        // if not using standard font size, Font must be defined below, even if using set_font, or the message will truncate unpredictably.
        msg.graphics.font="Tahoma:14";        
        
        var X_width = msg.preferredSize[0];
        with(msg){preferredSize = [-1,-1]; characters = ~~(w_width/X_width); preferredSize[1] = -1;};
        msg.text = message;
    }

    if (delaySeconds>0){
        var btn = win.add("button", undefined, "OK (" + delaySeconds + ")");
    }
    else{
        var btn = win.add("button", undefined, "OK",{name: "ok"});      
    }

    var cancelled = false;

    // Function for button click
    btn.onClick = function () {
        cancelled = true;
        win.close();
        $.writeln("User clicked OK");
    };

    // Show the window
    set_font (win, "Tahoma:14");
    win.show();

    if (delaySeconds>0){
        // Countdown loop
        while (delaySeconds > 0 && !cancelled) {
            $.sleep(1000); // wait 1 second
            delaySeconds--;
            try {
                if(delaySeconds > 0){    
                    btn.text = "OK (" + delaySeconds + ")";
                }
                else {
                    btn.text = "OK";   
                }
                //win.update();  // Force the window to update
            } catch (e) {
                break; // window might be closed already
            }
        }

        // Close window after countdown, unless user clicked OK
        if (!cancelled) {
            win.close();
            $.writeln("Auto-closed after timeout");
        }
    }
} // End AutocloseAlert

function set_font (control, font) {
    for (var i = 0; i < control.children.length; i++) {
    if ("GroupPanel".indexOf (control.children[i].constructor.name) > -1)
    set_font (control.children[i], font);
    else
    control.children[i].graphics.font = font;
    }
}//--end set_font;


function CustomConfirm(message, title, DefaultBtn, Btn1Text, Btn2Text, Btn3Text, Btn4Text, bgcolor) {
    // Yes/No, OKOnly, OK/Cancel, Abort/Retry/Ignore, Yes/No/Cancel, Retry/Ignore, 
    title = title || "Script Alert";
    Btn1Text = Btn1Text || "";
    Btn2Text = Btn2Text || "";
    Btn3Text = Btn3Text || "";
    Btn4Text = Btn4Text || "";  
    DefaulBtn = DefaultBtn || 0;
    // Shift the buttons:
    if (Btn1Text===""){
        // Show at least one button
        Btn4Text="OK";
    }
    else if (Btn2Text===""){
        // Only one button
        Btn4Text=Btn1Text;
        Btn1Text = "";
        if (DefaultBtn===1){
            DefaultBtn=4;
        }
    }
    else if (Btn3Text===""){
        // Two Buttons
        Btn4Text=Btn2Text;
        Btn3Text=Btn1Text;
        Btn2Text = "";
        Btn1Text = "";
        if (DefaultBtn===1){
            DefaultBtn=3;
        }
        else if (DefaultBtn===2){
            DefaultBtn=4
        }
     }
    else if (Btn4Text===""){
        // Three Buttons
        Btn4Text=Btn3Text;
        Btn3Text=Btn2Text;
        Btn2Text = Btn1Text;
        Btn1Text = "";
        if (DefaultBtn===1){
            DefaultBtn=2;
        }
        else if (DefaultBtn===2){
            DefaultBtn=3;
        }
        else if (DefaultBtn===3){
            DefaultBtn=4;
        }            
    }
    bgcolor = bgcolor || "gray";
    var result = "";
    var win = new Window("dialog", title, undefined, {closeButton:false});
    win.margins = 10;
    win.orientation = "column";
    win.alignChildren = "left";
    if(bgcolor==="white"){
        win.graphics.backgroundColor = win.graphics.newBrush (win.graphics.BrushType.SOLID_COLOR, [1.0, 1.0, 1.0]);
    }
    // Dialog Width to be defined:
    // If using Tahoma:14, there is no reason to set w_width below 350. Min Width is determined by the 4 buttons, even if some are not visible. Values over 350 make the window wider. I like 450.
    var w_width = 450;
    var msg = win.add("statictext",  undefined, 'X', {multiline: true});
    // Must be set here or some of the message gets chopped off.
    msg.graphics.font="Tahoma:14";
    var X_width = msg.preferredSize[0];
    with (msg){preferredSize = [-1,-1]; characters = ~~(w_width/X_width); preferredSize[1] = -1;};
    msg.text=message
    var myButtonGroup = win.add ("group");
    myButtonGroup.alignment = "right";
    var Btn1 = myButtonGroup.add ("button", undefined, Btn1Text);
    if(Btn1Text===""){
        Btn1.visible=false;
    }
    var Btn2 = myButtonGroup.add ("button", undefined, Btn2Text);
    if(Btn2Text===""){
        Btn2.visible=false;
    }        
    var Btn3 = myButtonGroup.add ("button", undefined, Btn3Text);
    if(Btn3Text===""){
        Btn3.visible=false;
    }        
    var Btn4 = myButtonGroup.add ("button", undefined, Btn4Text);
    if(Btn4Text===""){
        Btn4.visible=false;
    }
    switch(DefaultBtn){
        case 1:
            win.defaultElement=Btn1;
            break;
        case 2:
            win.defaultElement=Btn2;
            break;            
        case 3:
            win.defaultElement=Btn3;
            break;            
        case 4:
            win.defaultElement=Btn4;                       
    }
// Event Listeners
    Btn1.onClick=function(){
        result = Btn1Text;
        win.close();
      return result;
    };   
    Btn2.onClick=function(){
        result = Btn2Text;
        win.close();
        return result;
    };
    Btn3.onClick=function(){
        result = Btn3Text;
        win.close();
        return result;
    };
    Btn4.onClick=function(){
        result = Btn4Text;
        win.close();
        return result;
    }
    // Show the window
    set_font (win, "Tahoma:14");
    win.show();
    return result;
} // End  CustomConfirm

// Calling functions:
AutocloseAlert("This short message will close in 5 seconds.", 5, "Autoclose Alert");

AutocloseAlert("This much longer message will wrap nicely and will stay open until you click the OK button");

switch(CustomConfirm("Super long text, just a test, FWIW. No need to panic. Just see what happens. Go with the flow ... \r\rThis is Line2", undefined, 2, "Abort", "Retry", "Ignore")){
    case "Abort":
        alert("You selected Abort")
        break;
     case "Retry":
        alert("You selected Retry")
        break;
      case "Ignore":
        alert("You selected Ignore")
}

 


Recommend making a minor tweak to the CustomConfirm() code.

The above works fine if your button text is short (Yes/No/Retry).

If the button text is longer, the window expands to fit the button text (which is nice and I didn't really expect), but the message still wraps beforre it needs to.
Recommend changing the line:

var w_width = 450;

to:

var w_width = Math.max(450, (Btn1Text.length+Btn2Text.length+Btn3Text.length+Btn4Text.length)*9);

Basically, just empirically derived, but it seems to work well in testing.