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

New problems:

  • I'm getting an error on the Btn4.OnClick line if I don't specify a value for Btn4Text. Previously, it was working. I tried surrounding the code with if(Btn4.ObjectValid()){ ...}, but then I get the same error "Undefined is not an object" on that line.
  • I thought I figured out the script returning undefined I was returning the button text as the result of the .onCLick function and not the result of the CustomConfirm function, but I tried passing the Btn1.onclick result to the main function return and it didn't work, and I don't know how to pass it the "selected" button.

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.