USER INTERFACE DIALOGS

In the previous post we created text on a circle in InDesign using a simple script. To make the script more user friendly, a user dialog is in order. There are a number of ways one can create a dialog, but InDesign users will find that for most purposes, the easy-to-create dialog provided by the Dialog object will do the job.

INDESIGN’S CUSTOM DIALOG

InDesign’s dialog object, available for scripts written in AppleScript, JavaScript (ExtendScript), and VisualBasic, creates a modal window with a wealth of user interface objects (widgets) available. A modal window stays on top of all other applications as long as it has focus. We will explore how this works in creating a user interface dialog for our TextOnACurve project (see previous post).

Overview

For the purpose of our TextOnACurve project we will want the user to provide the following information:

  • Text to place on the top portion of the selected oval (optional)
  • Text to place on the bottom portion of the selected oval (optional)
  • A paragraph style to apply to the text

It will assume that the user has a document open with an oval selected. It will allow the user to choose to provide text for either the top or the bottom portion of the selected oval, or both. This can be accomplished using an enabling group. Enabling groups are similar to a checkbox in that the user can check it to select one or more options. Unique to an enabling group is that it can act as a container for other user interaction fields or objects. The “child” objects are only active (enabled) if the enabling group’s checkbox is checked (checked state is true).

The choice of paragraph style will be provided by a dropdown listing the styles available for the active document.

START WITH A TEMPLATE

You can start with one of the following basic templates:

AppleScript

set dialogName to "Test Dialog"
tell application "Adobe InDesign CC 2017"
	--provide for restoration of user interaction level
	set origLevel to user interaction level of script preferences
	set user interaction level of script preferences to interact with all
	set dlgRef to make dialog with properties ¬
{name:dialogName, can cancel:true, label:dialogName}
	tell dlgRef
	   --add fields and widgets here
	end tell
	set userResponse to show dlgRef
	if userResponse = true then
	   --capture results from widgets
	end if
	destroy dlgRef
	set user interaction level of script preferences to origLevel
	--return captured results or throw error based on value of userResponse
	if (userResponse is false) then
		error ("User Cancelled")
	end if
end tell
--return statement here

JavaScript

var dialogName = "Test Dialog";
//provide for restoration of user interaction level
var origLevel = app.scriptPreferences.userInteractionLevel;
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.INTERACT_WITH_ALL;
var myDialog = app.dialogs.add({name:dialogName});
with(myDialog){
    //add fields and widgets here
}
 var userResponse = myDialog.show();
if (userResponse == true) {
//capture results from widgets
}
myDialog.destroy();
//reset interaction level
app.scriptPreferences.userInteractionLevel = origLevel;
//return captured results or throw error based on value of userResponse
if (userResponse == false) {
throw ("User Cancelled");
}
//return statement here

Notice how these templates provide the following basic functionality:

  • Makes sure that user interaction level of script preferences is set to allow a dialog (interact with all)
  • Creates the dialog
  • Sets a variable to capture user’s response to dialog (OK or Cancel)
  • Captures data from widgets if user’s response is true
  • Restores user interaction level of script preferences to its original value
  • If user cancelled out of dialog, throws an error

You may want to make a copy of your template and save it for later use. Save your copy as “Test Dialog”.

Test It

Copy the template of choice into your favorite script editor (AppleScript or ExtendScript). Compile and run the script. Click OK when the dialog opens.

screen capture of dialog created from template…Dialog with buttons created from template

Notice: If you click Cancel, the script will throw an unhandled error. This error can be taken care of when you turn the script into a handler (function) for your main script. It is easier to work with dialog creation code as its own script as you can easily test as you go along. Once you get it working the way you want it, turning it into a handler (function) is a simple process.

User Interaction Level: If user interaction level of script preferences is not set to interact with all, the dialog will not display. We make an extra effort to make sure that this setting is set back to its original level since this is an application setting that can affect other scripts.

ADDING WIDGETS

This is where the fun begins. A dialog window is divided into columns and rows. A column can create rows and all other widgets as “child” elements with the exception of another column. Try it. Add the following to your test dialog script under the comment “add fields and widgets here”

AppleScript

tell (make dialog column)
   set eGroup1 to make enabling group with properties {static label:"Text for Top", checked state:true}
   tell eGroup1
      set topField to make text editbox with properties {min width:200}
   end tell
end tell --column

JavaScript

with(dialogColumns.add()){
   var eGroup1 = enablingGroups.add({staticLabel:"Text for Top", checkedState:true});
   var topField = eGroup1.textEditboxes.add({minWidth:200});
}

Screen capture of dialog with enabling group added…Dialog with enabling group added

DROP DOWN

Next we need to add a dropdown to allow the user to select the paragraph style for the text. But before we can create the dropdown, we need a list (array) of string values to present to the user. For the purpose of this script, we want a list (array) of the paragraph style names available for the document (assuming there is a document open).

For AppleScript, the code for this needs to be inside the tell block for the application:

tell application "Adobe InDesign CC 2017"
   set docRef to document 1
   set styleNames to name of every paragraph style of docRef
   --...
end tell

For JavaScript, the code is similar without the need to define the application as this is set in the target field for the editor.

var styleNames = app.documents[0].paragraphStyles.everyItem().name;

With a list of stylenames available we can create the dropdown. Inside the dialog column created above, we can create a row to hold a title for the dropdown and the dropdown itself. The dialog column block will now read as follows:

AppleScript

tell (make dialog column)
   set eGroup1 to make enabling group with properties {static label:"Text for Top", checked state:true}
   tell eGroup1
      set topField to make text editbox with properties {min width:250}
   end tell
   tell (make dialog row)
      make static text with properties {static label:"Paragraph Style:"}
      set dropElement to make dropdown with properties {string list:styleNames, selected index:3}
   end tell
end tell --column

JavaScript3

with(dialogColumns.add()){
   var eGroup1 = enablingGroups.add({staticLabel:"Text for Top", checkedState:true});
   var topField = eGroup1.textEditboxes.add({minWidth:200});
   with (dialogRows.add()){
      staticTexts.add({staticLabel:"Paragraph Style: "});
      var dropElement = dropdowns.add({stringList:styleNames, selectedIndex:3});
   }
}

screen capture of dialog created with dropdown addedThe dialog with dropdown added.

Notice: By providing a value for the selected index of a dropdown, a default value can be established.

CAPTURING RESULTS

Because we have established variables to reference each of the fields as they were created in the dialog, it becomes a simple matter to place the values in variables which will later be passed to the main portion of the script. For this we will initialize a variable for the top text (topText) at the top of the script as an empty string. With this in place, we will place the code to set the user input values below the comment that reads “capture results from widgets.” Note that we need to make sure the enabling group is checked before getting the text contents of its text field.

AppleScript

--at top of script after tell statement to application
set topText to ""
--after capture results from widgets comment
if (checked state of eGroup1 is true) then
   set topText to edit contents of topField
end if
set styleIndex to ((selected index of dropElement) + 1)

ExtendScripts

--at top of script
var topText = ""
--after capture results from widget comment
if (eGroup1.checkedState == true) {
   var topText = topField.editContents;
}
var styleIndex = dropElement.selectedIndex;

You may want to test your script at this point to make sure it works as anticipated. Test the values for the variables by placing the variable(s) at the bottom of the script.

Notice: By providing a value for the selected index of a dropdown, a default value can be established.

FINISH THE DIALOG

To complete the dialog, you will want to add another enabling group to the dialog (eGroup2). Notice that the script assumes that only one paragraph style will be used for both the top and bottom text. Should you want the user to select a paragraph style for each of the text options, you will need to have a dropdown inside each of the enabling groups.

Once you have the dialog working as you want, place the code for creating the dialog inside wrappers for a handler (function). Add code to pass the results from the dialog back to the main portion of the script. You will want to put this inside of a try statement block to handle the error that is passed back if the user clicks Cancel.

Here it is for AppleScript:

TextOnAPath (partial script)

set dialogName to "Test Dialog"
tell application "Adobe InDesign CC 2017"
   try
      set selItem to my getSelection(oval)
      set docRef to document 1
      set styleNames to name of every paragraph style of docRef
      set dialogResult to my customDialog(dialogName, styleNames)
      copy dialogResult to {topText, bottomText, styleIndex}
      set paraStyleRef to paragraph style (styleIndex) of docRef
      {topText, bottomText, paraStyleRef} --for testing
   on error errMsg
      display alert errMsg
      return
   end try
   --...
end tell
(*Dialog provides fields for entering text and a dropdown for user choice*)
on customDialog(dialogName, styleNames)
   set topText to ""
   set bottomText to ""
   tell application "Adobe InDesign CC 2017"
      --provide for restoration of user interaction level
   set origLevel to user interaction level of script preferences
   set user interaction level of script preferences to interact with all
   set dlgRef to make dialog with properties {name:dialogName, can cancel:true, label:dialogName}
   tell dlgRef
      tell (make dialog column)
         set eGroup1 to make enabling group with properties¬ 
{static label:"Text for Top", checked state:true}
         tell eGroup1
            set topField to make text editbox with properties {min width:250}
         end tell
         set eGroup2 to make enabling group with properties ¬
{static label:"Text for Bottom", checked state:false}
         tell eGroup2
            set bottomField to make text editbox with properties {min width:250}
         end tell
      tell (make dialog row)
         make static text with properties {static label:"Paragraph Style:"}
         set dropElement to make dropdown with properties ¬
{string list:styleNames, selected index:3}
      end tell
   end tell --column
   end tell --dialog
   set userResponse to show dlgRef
   if userResponse = true then
      --capture results from widgets
      if (checked state of eGroup1 is true) then
         set topText to edit contents of topField
      end if
      if (checked state of eGroup2 is true) then
         set bottomText to edit contents of bottomField
      end if
      set styleIndex to ((selected index of dropElement) + 1)
   end if
   destroy dlgRef
   set user interaction level of script preferences to origLevel
   --return captured results or throw error based on value of userResponse
   if (userResponse is false) then
      error ("User Cancelled")
   end if
   end tell
      return {topText, bottomText, styleIndex}
end customDialog
(*The handler. tests for selection and class. Throws error if no selection exists or first item of selection is mot of type passed to handler as its only argument *)
on getSelection(classType)
   tell application "Adobe InDesign CC 2017"
      set selList to selection
      if length of selList > 0 and class of item 1 of selList is classType then
         return item 1 of selList
      else
         error "Requires oval selection"
      end if
   end tell
end getSelection

screen capture of the finished dialog…The finished dialog

ON YOUR OWN

ALL YOU NEED NOW IS THE CODE THAT USES THE INFORMATION FROM THE DIALOG TO PLACE THE TEXT ON THE PATH FOR THE SELECTED OVAL. SEE THE SCRIPT FROM OUR PREVIOUS BLOG POST. THE CHALLENGE IS FOR YOU TO COMPLETE THE SCRIPT. NOTICE THAT WE INCLUDED THE GETSELECTION HANDLER FROM THAT SCRIPT AS PART OF THE SCRIPT ABOVE.

ONWARD AND UPWARD

We will be looking at other dialog elements (widgets) in future blog posts. Meanwhile, AppleScript users will want to take a look at the script Dictionary for their version of InDesign. The listing for the dialog and dialog elements is found in the UI suite. For JavaScript users, open the Object Model Viewer for ExtendScript. With your version of Adobe InDesign selected take a look at the Dialog and DialogColumn objects.

 

Disclaimer:
Scripts are provided to help users create their own real world scripts. No representation is made as to the completeness or reliabiity of the scripts. Use at your own risk.