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
April 15, 2025

Have the script working, the problem was in the calling procedure. Here's the sloppy script with commented lines still included:

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;
    bgcolor = bgcolor || "gray";
    var mainresult = "";
    var win = new Window("dialog", title, undefined, {closeButton:false});
    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]);
    }
    var msg = win.add("statictext", undefined, message);
    var myButtonGroup = win.add ("group");
    myButtonGroup.alignment = "center";
    var Btn1 = myButtonGroup.add ("button", undefined, Btn1Text);
            if(Btn1Text===""){
            Btn1.visible=false;
        }
//    if(Btn2Text!==""){
        var Btn2 = myButtonGroup.add ("button", undefined, Btn2Text);
        if(Btn2Text===""){
            Btn2.visible=false;
        }        
//    }
 //   if(Btn3Text!==""){
        var Btn3 = myButtonGroup.add ("button", undefined, Btn3Text);
        if(Btn3Text===""){
            Btn3.visible=false;
        }        
//    }
//    if(Btn4Text!==""){
        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;
            break;            
    }
//alert(Btn4.ObjectValid())
// Event Listeners
    Btn1.onClick=function(){
//        alert(Btn1Text);
        result = Btn1Text;
//alert(result);
//        return result;
        win.close();
         $.writeln("User clicked Abort");
         $.writeln(Btn1Text);
      return result;
    };   
    Btn2.onClick=function(){
        result = Btn2Text;
        win.close();
         $.writeln("User clicked Retry");
         $.writeln(Btn2Text);
        return result;
    };
    Btn3.onClick=function(){
        result = Btn3Text;
        win.close();
         $.writeln("User clicked Ignore");
         $.writeln(Btn3Text);
        return result;
    };


        Btn4.onClick=function(){
            result = Btn4Text;
            win.close();
 //            $.writeln("User clicked Retry");
 //            $.writeln(Btn4Text);
            return result;
        }


    // Show the window
    set_font (win, "Tahoma:14");
    win.show();
 //   alert(Btn1.onClick())
    mainresult = result
//    alert (mainresult)
 $.writeln(mainresult);    
    return mainresult;

} // End  CustomConfirm

Calling functions:

//alert(CustomConfirm("Select A Button:", undefined, 2, undefined, "Abort", "Retry", "Ignore"))

Returns what was selected ...

var answer = CustomConfirm("Select A Button:", undefined, 2, undefined, "Abort", "Retry", "Ignore");
//alert(answer);
if(answer==="Abort"){
    alert("You selected Abort");
    }
else if(answer==="Retry"){
    alert("You selected Retry");
    }
else if(answer==="Ignore"){
    alert("You selected Ignore");
    }

Above works perfectly:

var answer = CustomConfirm("Select A Button:", undefined, 2, undefined, "Abort", "Retry", "Ignore");
switch(answer){
    Case "Abort"
        alert("You selected Abort")
        break;
     Case "Retry"
        alert("You selected Retry")
        break;
      Case "Ignore"
        alert("You selected Ignore")
        break;
    }

Above does nothing, not sure why ...

if(CustomConfirm("Select A Button:", undefined, 2, undefined, "Abort", "Retry", "Ignore")==="Abort"){
    alert("You selected Abort");
    }
else if(CustomConfirm("Select A Button:", undefined, 2, undefined, "Abort", "Retry", "Ignore")==="Retry"){
    alert("You selected Retry");
    }
else if(CustomConfirm("Select A Button:", undefined, 2, undefined, "Abort", "Retry", "Ignore")==="Ignore"){
    alert("You selected Ignore");
    }

Above is awkward. If you are clicking Button 3, you have to click it three times (the pop-up reappears) and then it works.
To Do:

  • The buttons should be right aligned. The script still creates 4 buttons, but the buttons are hidden if they don't have text. So if you have only an OK button, the text will show up on button 1, which is to the left. I need to shift the buttons over. I'll post updated code soon.

Fixed it. Final code (it could be prettier/more efficient, but it works):

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.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]);
    }
    var msg = win.add("statictext", undefined, 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 Code (I'm not sure why switch doesn't work, but ...):

var answer = CustomConfirm("Select A Button:", undefined, 2, "Retry", "Ignore");
// Include up to 4 buttons in the order you want them to appear. Set Default Button to 0 if you don't want a default.
if(answer==="Retry"){
    alert("You selected Retry");
}
else if(answer==="Ignore"){
    alert("You selected Ignore");
}

Alernative (simpler) calling statement:

switch(CustomConfirm("Select A Button:", undefined, 2, "Abort", "Retry", "Ignore")){
// Include up to 4 buttons in the order you want them to appear. Set Default Button to 0 if you don't want a default.
    case "Abort":
        alert("You selected Abort")
        break;
     case "Retry":
        alert("You selected Retry")
        break;
      case "Ignore":
        alert("You selected Ignore")
}

 

Advantages over standard Confirm dialog (or even VBA Enhanced Message Box):

  • You can have up to 4 buttons. Probably overkill, but there are times I would have liked it with VBA. At least 3 (Yes/No/Cancel,  Abort/Retry/Ignore) is handy.
  • You can specify ANY name for the buttons.
  • Result matches the BTN name. (If you use custom button names with EMB, it returns vbBt3 and you have to figure out which button that is.)
  • Can specify any button as the default button - or no default button.

Disadvantage:

  • Kbd Enter and Kbd Esc don't seem to do anything in FrameMaker. I think they do work in ExtendScriptToolKit. (There seem to be a lot of things that don't work in FrameMaker but do work in ScriptUI, for whatever reason.)