Skip to content
This repository has been archived by the owner on Jun 3, 2022. It is now read-only.

Width dialog box #6

Open
ghost opened this issue Nov 7, 2018 · 6 comments
Open

Width dialog box #6

ghost opened this issue Nov 7, 2018 · 6 comments

Comments

@ghost
Copy link

ghost commented Nov 7, 2018

How set width for dialog box?

@gen2brain
Copy link
Owner

AppleScript and zenity/qarma do not support that, and for Windows there are some hardcoded values.

@gabyx
Copy link
Contributor

gabyx commented Feb 9, 2021

Indeed zenity supports --no-wrap which is nice, such that the user can define how long the window is by

zenity --question --text "line line line line\nlinelinelinelinelinelinesssssssssssssssssssssssssssssss\nlinelineline" --no-wrap

It would be nice to support some options like this. Of course it will only work for zenity. Maybe provide a text word wrap functionality inside such that, a predefined with is computed for other implementation, and for zenity it is directly passed through?

image

@gabyx
Copy link
Contributor

gabyx commented Feb 9, 2021

I wanted to use this implementation for https://github.com/gabyx/githooks, but I need such a functionality somehow, that the size can be controlled somehow. Maybe we can simulate the width for zenity, by using always --no-wrap and wrapping the input text between a word after a total of chars have been skipped summing up approximately to the desired units width. ?

@gabyx
Copy link
Contributor

gabyx commented Feb 9, 2021

@giladreich
Copy link
Contributor

It's a bit of a tricky topic, hence I introduced the option in this PR to create a message box without an icon in order to win some space: #30

  • Windows - Most likely it will require hooking into WinAPI's MessageBox thread on launch and set some style properties, such as SS_WORDELLIPSIS

  • Linux - Zenity --no-wrap seems like a simple way to approach this

  • Darwin - Here is an old thread: https://www.macscripter.net/viewtopic.php?id=47882

    osascript code
    -- These "use" statements are required in the script containing the displayDialog handler
    use framework "Foundation"
    use scripting additions
    
    -- The following example shows that the only required input record property is "dialogText"; default values will be supplied for all missing optional properties, just as is the case with the standard "display dialog" command
    
    set myDialogText to "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do." & return & "Phasellus vestibulum lorem sed risus, integer eget aliquet eiusmod tempor praesent tristique." & return & "Ornare quam viverra orci sagittis, purus viverra accumsan in nisl nisi scelerisque eu, condimentum mattis pellentesque id nibh."
    
    display dialog myDialogText
    --> the dialog text's three sentences wrap onto seven lines in AppleScript's standard dialog window
    
    displayDialog({dialogText:myDialogText})
    --> the dialog text's three sentences don't wrap in the widened dialog window produced by the displayDialog handler
    
    -- Just to have some fun and show that the handler can handle multibyte characters without problems
    
    set myDialogText to "┇‡⋘⋙▉⫶·±⊕⊞§÷צôt↓➨┇‡⋘⋙▉⫶·±⊕⊞§÷צôt↓➨°½②①éⵘ≅─⌘⌥┇‡⋘⋙▉⫶┇‡⋘⋙▉⫶·±⊕⊞§÷צôt↓➨‡⋘⋙▉⫶·±⊕⊞§÷צôt↓➨┇‡⋘⋙▉⫶·±⊕⊞§÷צôt↓➨°½②①éⵘ≅─⌘⌥┇‡⋘⋙▉⫶┇‡⋘⋙▉⫶·±⊕⊞§÷צôt↓➨"
    set myButtons to {"Quit", "Proceed"}
    set myDefaultButton to "Proceed"
    
    display dialog myDialogText buttons myButtons default button myDefaultButton
    --> the dialog text wraps onto four lines in AppleScript's standard dialog window
    
    displayDialog({dialogText:myDialogText, theButtons:myButtons, defaultButton:myDefaultButton})
    --> the dialog text doesn't wrap in the widened dialog window produced by the displayDialog handler
    
    -- An example with a default answer, a window icon, and the special three-button configuration that produces increased spacing between the first and second window buttons
    
    set myDialogText to "Make any desired changes:"
    set myDefaultAnswer to "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud."
    set myButtons to {"Maybe", "No", "Yes"}
    set myDefaultButton to "Yes"
    set myCancelButton to "No"
    set myTitle to "Silly Example"
    set myIcon to caution
    
    display dialog myDialogText default answer myDefaultAnswer buttons myButtons default button myDefaultButton cancel button myCancelButton with title myTitle with icon myIcon
    --> the default answer wraps onto four lines in AppleScript's standard dialog window
    
    displayDialog({dialogText:myDialogText, defaultAnswer:myDefaultAnswer, theButtons:myButtons, defaultButton:myDefaultButton, cancelButton:myCancelButton, withTitle:myTitle, withIcon:myIcon})
    --> the default answer doesn't wrap in the widened dialog window produced by the displayDialog handler
    
    ------------------------------------------------------------------------------------------------------------------------------------
    on displayDialog(inputRecord)
       -- Replicates the behavior of AppleScript's "display dialog" command, except that the dialog window is widened as necessary to prevent any wrapping of dialog and/or default answer text lines
       script main
           -- "display dialog" window properties
           property dialogTextFontAttribute : missing value -- the font used for the window's dialog text
           property hiddenAnswerChar : missing value -- the character used to hide the default answer text
           property hiddenAnswerCharWidth : missing value -- the pixel width of the hidden answer character
           property nonwidenedDialogWindowWidth : missing value -- the pixel width of a standard, non-widened dialog window
           property buttonToWindowWidth : missing value -- the pixel width of the space between each outermost button and the adjacent window margin
           property buttonToButtonWidth : missing value -- the pixel width of the space between buttons
           property buttonToLabelWidth : missing value -- the pixel width of the space between either end of a button label and the adjacent button margin
           property iconAdjustmentWidth : missing value -- the pixel width of the extra space between the leftmost button and the adjacent window margin due to the presence of a window icon
           property specialThreeButtonAdjustmentWidth : missing value -- the pixel width of the extra space between the first and second buttons when all of the following conditions apply: (A) there are three buttons, (B) the rightmost button is the default button, and (C) the middle button is the cancel button
           -- Other properties
           property labelWideningChar : missing value -- the space character that will be replicated to form the left and right pads used to widen button labels (will be assigned the HAIR SPACE character = character id 8202)
           property labelWideningCharWidth : missing value -- the pixel width of the button label-widening character
           property labelTerminatingChar : missing value -- the character that will added to the beginning and end of widened button labels to prevent off-center positioning of labels in the button frame (will be assigned the TAG LATIN SMALL LETTER A character = character id 917601, the only [?] non-displaying Unicode character that prevents off-center positioning)
           property labelTerminatingCharWidth : missing value -- the pixel width of the button label-terminating character
           property screenWidth : missing value -- the pixel width of the display screen
           -- Utility handlers
           on getStringWidth(theString)
               -- Returns the pixel width of a string in the font used for the "display dialog" window's dialog text = NSFont's system font of default size
               return ((current application's NSString's stringWithString:theString)'s sizeWithAttributes:(my dialogTextFontAttribute))'s width as real
           end getStringWidth
           on replicateCharacter(theChar, nReps)
               -- Returns a string consisting of a character replicated a specified number of times
               return ((current application's NSString's stringWithString:"")'s stringByPaddingToLength:nReps withString:theChar startingAtIndex:0) as text
           end replicateCharacter
           on textRep(theValue)
               -- Returns the text representation of any AppleScript value
               try
                   || of {theValue}
               on error m
                   try
                       set {tid, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "{"}
                       set m to m's text items 2 thru -1 as text
                       set AppleScript's text item delimiters to "}"
                       set valueAsText to m's text items 1 thru -2 as text
                       set AppleScript's text item delimiters to tid
                   on error
                       set AppleScript's text item delimiters to tid
                       error "Problem with utility handler textRep:" & return & return & "Could not get the text representation of the input value."
                   end try
               end try
               return valueAsText
           end textRep
           on widenButtonLabel(theLabel, labelWidth, targetWidth)
               -- Widens a button label to a target pixel width using left and right space character pads along with a special character at either end of the widened label, the latter to prevent spurious off-center positioning of the label within the button frame
               -- Notes:
               -- The HAIR SPACE character is the narrowest Unicode space character available for the creation of the space pads and thus is used to achieve the most precise widening possible
               -- The special character TAG LATIN SMALL LETTER A is added to the beginning and end of the widened label to prevent spurious off-center positioning of the label within the button frame; it is the only non-displaying Unicode character discovered to date that fulfills this function; alternatively, any visible character may be used
               set nLabelWideningSpaceChars to ((targetWidth - labelWidth - 2 * (my labelTerminatingCharWidth)) / (my labelWideningCharWidth)) as integer
               set modifiedLabel to theLabel
               if nLabelWideningSpaceChars > 0 then
                   set nLeftSpaceChars to nLabelWideningSpaceChars div 2
                   set nRightSpaceChars to nLabelWideningSpaceChars - nLeftSpaceChars
                   set modifiedLabel to (my labelTerminatingChar) & (my replicateCharacter(my labelWideningChar, nLeftSpaceChars)) & theLabel & (my replicateCharacter(my labelWideningChar, nRightSpaceChars)) & (my labelTerminatingChar)
               end if
               return modifiedLabel
           end widenButtonLabel
           on run
               -- Assign constant values to the "display dialog" window properties
               set my dialogTextFontAttribute to current application's NSDictionary's dictionaryWithObject:(current application's NSFont's systemFontOfSize:0) forKey:(current application's NSFontAttributeName)
               set my hiddenAnswerChar to character id 9679 -- 9679 = the black circle character used by the "display dialog" window to hide the default answer's text when that option is specified
               set my hiddenAnswerCharWidth to my getStringWidth(my hiddenAnswerChar)
               set my nonwidenedDialogWindowWidth to 420
               set my buttonToWindowWidth to 22
               set my buttonToButtonWidth to 12
               set my buttonToLabelWidth to 12
               set my iconAdjustmentWidth to 78
               set my specialThreeButtonAdjustmentWidth to 26
               -- Assign constant values to the other properties
               set my labelWideningChar to character id 8202
               set my labelWideningCharWidth to my getStringWidth(my labelWideningChar)
               set my labelTerminatingChar to character id 917601
               set my labelTerminatingCharWidth to my getStringWidth(my labelTerminatingChar)
               set my screenWidth to current application's NSScreen's mainScreen()'s frame()'s second item's first item as real
               -- Validate and process the input record
               try
                   tell (inputRecord & {defaultAnswer:missing value, hiddenAnswer:missing value, theButtons:missing value, defaultButton:missing value, cancelButton:missing value, withTitle:missing value, withIcon:missing value, givingUpAfter:missing value})
                       if length  9 then error
                       set {dialogText, defaultAnswer, hiddenAnswer, theButtons, defaultButton, cancelButton, withTitle, withIcon, givingUpAfter} to {its dialogText, its defaultAnswer, its hiddenAnswer, its theButtons, its defaultButton, its cancelButton, its withTitle, its withIcon, its givingUpAfter}
                   end tell
               on error
                   error "The handler input argument must be a record with the following properties:" & return & return & "Required property (equivalent to \"display dialog\"'s direct parameter):" & return & return & tab & "dialogText" & return & return & "Optional properties (equivalent to \"display dialog\"'s analogously named optional parameters):" & return & return & tab & "defaultAnswer" & return & tab & "hiddenAnswer" & return & tab & "theButtons" & return & tab & "defaultButton" & return & tab & "cancelButton" & return & tab & "withTitle" & return & tab & "withIcon" & return & tab & "givingUpAfter"
               end try
               -- Validate and process the input record properties
               if dialogText's class  text then error "The input property dialogText must be a text string."
               tell defaultAnswer to if (it  missing value) and (its class  text) then error "The input property defaultAnswer must be the missing value or a text string."
               tell hiddenAnswer to if (it  missing value) and (its class  boolean) then error "The input property hiddenAnswer must be the missing value or a boolean true or false value."
               tell theButtons
                   if it = missing value then
                       set {theButtons, defaultButton, cancelButton} to {{"Cancel", "OK"}, 2, 1}
                   else if its class  list then
                       set theButtons to {it}
                   end if
               end tell
               tell theButtons
                   try
                       if (length < 1) or (length > 3) then error
                       repeat with i from 1 to length
                           set currButton to item i
                           if currButton's class  text then error
                           if currButton = defaultButton then set defaultButton to i
                           if currButton = cancelButton then set cancelButton to i
                       end repeat
                       set nButtons to length
                   on error
                       error "The input property theButtons must be the missing value, a text string (i.e., one button), or a list of one to three text strings."
                   end try
               end tell
               try
                   tell defaultButton to if not ((it = missing value) or ((its class = integer) and (it  1) and (it  nButtons))) then error
               on error
                   error "The button specified by the input property defaultButton does not exist."
               end try
               try
                   tell cancelButton to if not ((it = missing value) or ((its class = integer) and (it  1) and (it  nButtons))) then error
               on error
                   error "The button specified by the input property cancelButton does not exist."
               end try
               tell withTitle to if (it  missing value) and (its class  text) then error "The input property withTitle must be the missing value or a text string."
               tell withIcon
                   try
                       if not (({it} is in {missing value, stop, note, caution, 0, 1, 2}) or ((its class = alias) and ((it as text) ends with ".icns"))) then error
                   on error
                       error "The input property withTitle must be one of the following values:" & return & tab & "missing value" & return & tab & "stop or 0" & return & tab & "note or 1" & return & tab & "caution or 2" & return & tab & "AppleScript alias to a \".icns\" file"
                   end try
               end tell
               tell givingUpAfter
                   try
                       if (it  missing value) and (its class  integer) then set givingUpAfter to it as integer
                   on error
                       error "The input property givingUpAfter can't be transformed into an integer."
                   end try
               end tell
               -- Get the maximum pixel width of the dialog and default answer text lines; in the case of a hidden default answer, this value consists of the pixel width of a string of black circle characters (character id 9679) replicated to the number of characters in the default answer's text
               set maxTextWidth to 0
               repeat with currLine in (get dialogText's paragraphs)
                   tell my getStringWidth(currLine's contents) to if it > maxTextWidth then set maxTextWidth to it
               end repeat
               tell defaultAnswer
                   if it  missing value then
                       if hiddenAnswer = true then
                           tell length * (my hiddenAnswerCharWidth) to if it > maxTextWidth then set maxTextWidth to it
                       else
                           repeat with currLine in (get its paragraphs)
                               tell my getStringWidth(currLine's contents) to if it > maxTextWidth then set maxTextWidth to it
                           end repeat
                       end if
                   end if
               end tell
               -- If an icon is to be displayed in the "display dialog" window, account for the increased width between the leftmost button and the left window margin
               set buttonToWindowAdjustmentWidth to 0
               if withIcon  missing value then set buttonToWindowAdjustmentWidth to my iconAdjustmentWidth
               -- Determine if the "display dialog" window needs to be widened to prevent wrapping of dialog and/or default answer text
               set windowNeedsWidening to maxTextWidth > ((my nonwidenedDialogWindowWidth) - 2 * (my buttonToWindowWidth) - buttonToWindowAdjustmentWidth)
               -- If the window needs to be widened, do so by widening the window's button labels
               set modifiedButtons to theButtons
               if windowNeedsWidening then
                   -- Don't widen the "display dialog" window beyond the display screen's width; this is accomplished by setting an upper limit to the maximum text width value
                   tell ((my screenWidth) - 2 * (my buttonToWindowWidth) - buttonToWindowAdjustmentWidth) to if maxTextWidth > it then set maxTextWidth to it
                   -- Account for an increased width between the first and second buttons if all of the following conditions apply: (A) there are three buttons, (B) the rightmost button is the default button, and (C) the middle button is the cancel button
                   set buttonToButtonAdjustmentWidth to 0
                   if (defaultButton = 3) and (cancelButton = 2) then set buttonToButtonAdjustmentWidth to my specialThreeButtonAdjustmentWidth
                   -- Calculate the target width for button labels that will widen the "display dialog" window just enough to prevent text wrapping
                   set targetWidth to (maxTextWidth + (my buttonToButtonWidth) - buttonToButtonAdjustmentWidth) / nButtons - (my buttonToButtonWidth) - 2 * (my buttonToLabelWidth)
                   -- Widen each button label to the target width
                   set modifiedButtons to {}
                   repeat with currButton in theButtons
                       set end of modifiedButtons to my widenButtonLabel(currButton's contents, my getStringWidth(currButton's contents), targetWidth)
                   end repeat
               end if
               -- Create a text representation of the "display dialog" command, incorporating any optional parameters specified in the input record
               set theCommand to "activate" & return & "display dialog " & my textRep(dialogText) & " buttons " & my textRep(modifiedButtons)
               if defaultButton  missing value then set theCommand to theCommand & " default button " & my textRep(defaultButton)
               if cancelButton  missing value then set theCommand to theCommand & " cancel button " & my textRep(cancelButton)
               if defaultAnswer  missing value then set theCommand to theCommand & " default answer " & my textRep(defaultAnswer)
               if hiddenAnswer  missing value then set theCommand to theCommand & " hidden answer " & my textRep(hiddenAnswer)
               if withTitle  missing value then set theCommand to theCommand & " with title " & my textRep(withTitle)
               if withIcon  missing value then set theCommand to theCommand & " with icon " & my textRep(withIcon)
               if givingUpAfter  missing value then set theCommand to theCommand & " giving up after " & my textRep(givingUpAfter)
               -- Execute the text representation of the "display dialog" command with a "run script" command, and capture the returned values
               set returnedValues to (run script theCommand)
               -- Set the "button returned" return value to the original button name, not the widened button name
               if windowNeedsWidening then
                   set buttonReturned to returnedValues's button returned
                   repeat with i from 1 to modifiedButtons's length
                       if modifiedButtons's item i = buttonReturned then
                           set returnedValues's button returned to theButtons's item i
                           exit repeat
                       end if
                   end repeat
               end if
               -- Return the "display dialog" command's returned values to the calling program
               return returnedValues
           end run
       end script
       -- Wrap the entire executed handler code in a try block so that any execution error may be captured and displayed
       try
           run main
       on error m number n
           if n = -128 then error number -128
           if n  -2700 then set m to "(" & n & ") " & m
           tell application "System Events"
               activate
               display dialog ("Problem with handler displayDialog:" & return & return & m) buttons "OK" default button "OK" cancel button "OK"
           end tell
       end try
    end displayDialog

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants