Thank you very much red_menace.
I tried both delegate methods and, If I understood well, in this particular case I have to choose between the two, since having both doesn’t make sense: if I’m prevented to add non acceptable characters from the beginning it doesn’t make sense to me to check the textfield after the editing, right?
I added the first one (controltextdidchange) and it works nice, but I don’t understand how to get an error notification (I can hear NSBeep ()); I tried to insert a display dialog but it feels kinda laggy after first error, and I have to exit and enter again the textfield to write again any character. This is how I updated the test script:
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use script "Dialog Toolkit Plus" version "1.1"
use script "Dialog Toolkit Plus Extended" version "1.1" -- datePicker
property popupItems : {"This Thing", "That Thing", "The Other Thing"} -- the operations to perform
global controlGroups -- this will be a dictionary of the alternate groups and their controls
# Check out additions to the number formatted text field.
on controlTextDidChange:notificationObject
set acceptable to characters of "1234567890E,+- " -- or whatever
set object to notificationObject's object -- the textField that posted the notification
set theText to object's stringValue as text
repeat with aCharacter in (characters of theText) -- just check everything in case something was pasted
considering case -- for the exponent indicator `E`
if aCharacter is not in acceptable then -- indicate and remove
current application's NSBeep()
set {prevTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, aCharacter}
set {textItems, AppleScript's text item delimiters} to {text items of theText, ""}
set {theText, AppleScript's text item delimiters} to {textItems as text, prevTID}
(object's setStringValue:theText)
tell application "System Events"
display dialog "Character not acceptable" with icon caution
end tell
end if
end considering
end repeat
end controlTextDidChange:
on run -- example
set controlGroups to current application's NSMutableDictionary's dictionary -- for using a keyPath
set {viewWidth, theTop} to {400, 28}
# controls are stacked from the bottom up for easier vertical positioning
set {theButtons, minWidth} to create buttons {"Cancel", "OK"} default button "OK" cancel button "Cancel" with equal widths
if minWidth > viewWidth then set viewWidth to minWidth
set {maxLabelWidth, controlLeft} to {100, 110}
########## begin control group #1 - these are layered in the same space, with only the active alternate shown
set newTop to theTop -- alternate A (This Thing)
set {A2_textField, A2_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop) total width viewWidth label text "This #2:" field left controlLeft
set {A1_textField, A1_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop + 6) total width viewWidth label text "This #1:" field left controlLeft
repeat with anItem in (addAlternate(1, first item of popupItems, {A1_textField, A1_labelField, A2_textField, A2_labelField}))
(anItem's setHidden:true)
end repeat
----
set newTop to theTop -- alternate B (That Thing)
set {B3_checkbox, newTop, newWidth} to create checkbox "That Checkbox" bottom (newTop - 24) max width (viewWidth / 2) left inset controlLeft without initial state
set {B2_textField, B2_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop + 6) total width viewWidth label text "That #2:" field left controlLeft
set {B1_textField, B1_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop + 6) total width viewWidth label text "That #1:" field left controlLeft
repeat with anItem in (addAlternate(1, second item of popupItems, {B1_textField, B1_labelField, B2_textField, B2_labelField, B3_checkbox}))
(anItem's setHidden:true)
end repeat
########## end control group #1
set theTop to newTop -- continue from previous top
set {divider, theTop} to create rule (theTop + 12) rule width viewWidth -- NSBox
set {invoiceTextField, invoiceLabelField, theTop, fieldLeft} to create side labeled field ("") bottom (theTop + 12) total width (viewWidth / 2) label text "Invoice number:" field left controlLeft
set {contractorTextField, contractorLabelField, theTop, fieldLeft} to create side labeled field ("") bottom (theTop + 12) total width viewWidth label text "Contractor:" field left controlLeft
set {clientTextField, clientLabelField, theTop, fieldLeft} to create side labeled field ("") bottom (theTop + 12) total width viewWidth label text "Client:" field left controlLeft
set {dateLabelField, theTop} to create label " Date:" left inset 60 bottom (theTop + 12) max width maxLabelWidth control size regular size
set {datePicker, theTop} to create textual datepicker textual stepper picker elements YearMonthDay initial date (current date) min date seconds 0 max date seconds 0 left inset controlLeft bottom (theTop - 20) extra width 0 extra height 0
set {operationPopup, theTop} to create popup popupItems left inset controlLeft bottom (theTop + 8) popup width 180 initial choice (last item of popupItems)
operationPopup's setToolTip:"the operation to perform"
operationPopup's setTarget:me
operationPopup's setAction:"popupAction:" -- choose from group #1
A2_textField's setDelegate:me
set allControls to {operationPopup, dateLabelField, datePicker, clientLabelField, clientTextField, contractorLabelField, contractorTextField, invoiceLabelField, invoiceTextField, divider, A1_labelField, A1_textField, A2_labelField, A2_textField, B1_labelField, B1_textField, B2_labelField, B2_textField, B3_checkbox} -- control references are listed from top to bottom for easier indexing
set {buttonName, controlValues} to display extended window "Descriptive Title" acc view width viewWidth acc view height theTop acc view controls allControls buttons theButtons without align cancel button
doStuff for (formatResult from controlValues)
end run
# Extract the desired controls from the returned dialog values and group into a record.
to formatResult from dialogResult
tell (date (item 3 of dialogResult) as «class isot» as string) to set ISODate to text 9 thru 10 & "/" & text 6 thru 7 & "/" & text 1 thru 4 -- format date using the built-in ISO date class (no time zone info)
tell dialogResult -- use record for consistent key:value pairs
set common to {operation:item 1, theDate:ISODate, client:item 5, contractor:item 7, invoice:item 9}
if first item is first item of popupItems then -- common + alternate A
set results to common & {A1_textField:item 12, A2_textField:item 14}
else if first item is second item of popupItems then -- common + alternate B
set results to common & {B1_textField:item 16, B2_textField:item 18, B3_checkbox:item 19}
else if first item is third item of popupItems then
set results to common
else -- oops
set results to missing value
end if
end tell
return results
end formatResult
# Add a set of alternate controls to the controlGroups dictionary - controls are returned for any customization.
to addAlternate((group as text), (keyName as text), (controls as list))
if (controlGroups's valueForKey:group) is missing value then (controlGroups's setValue:(current application's NSMutableDictionary's dictionary()) forKey:group) -- add new group as needed
controlGroups's setValue:(current application's NSDictionary's dictionaryWithDictionary:{controls:controls}) forKeyPath:(group & "." & keyName) -- add keyName alternate controls to group
return controls
end addAlternate
# Action to change the visibility of group #1 controls based on the popup value.
on popupAction:sender
repeat with aKey in popupItems -- hide everything
set controlList to (controlGroups's valueForKeyPath:("1." & aKey & ".controls"))
if controlList is not missing value then repeat with aControl in controlList
((contents of aControl)'s setHidden:true)
end repeat
end repeat
set controlList to (controlGroups's valueForKeyPath:("1." & ((sender's title) as text) & ".controls"))
if controlList is not missing value then repeat with aControl in controlList -- show the selected control group
((contents of aControl)'s setHidden:false)
end repeat
end popupAction:
# Do stuff for the dialog result values.
to doStuff for dialogValues
tell (operation of dialogValues) to if it is first item of popupItems then
-- This
else if it is second item of popupItems then
-- That
else if it is third item of popupItems then
-- The Other
end if
return dialogValues
end doStuff
As I told you I tried the only method I know - display dialog - but it doesn’t perform well, but I’m sure there is a method for this
Second question: is the
A2_textField's setDelegate:me
pasted in the right position? It works, but I don’t know if there is a more appropriate place to paste.
Third question: just in case I would like to use this method to limit another textfield, let’s say A1, with different characters (let’s say I would like it to accept only letters and not numbers - just an example to differentiate), how can I do that? I tried duplicating the controltextdidchange but, as you can imagine, it didn’t work because I stuck at the way to refer the first to A1 textfield and the second to A2 textfield
Apart from this, I finished my first version of the script and it seems to work very nice thanks to your huge help
- otherwise I would not have been able to script it, this is an handy addition I would like to add to improve it