Adding accessory views to Myriad Tables with Dialog Toolkit Plus

After a little more tooling around with Myriad Tables and Dialog Toolkit Plus, I’ve put together a script that demonstrates how to build and use and get results from Accessory Views in Myriad tables.

Keep in mind a couple things. First this is made for an appleScript writer (me) to be able to quickly figure out which accessory view to use, how to use it, and how to get its result.

So it’s a little klunky and doesn’t test to see if the height of all your views is greater than 250 (causes an error) but I find it useful.

Edit, the script below must be saved as an applet or a bundle (.scptd) before it runs or PrefsStorageLib will throw an error.

As always I’m very open to suggestions for improving it!


use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "Carbon"
use script "Dialog Toolkit Plus" version "1.1.2"
use script "Myriad Tables Lib" version "1.0.12"
use script "PrefsStorageLib" version "1.1.0"
use script "FileManagerLib" version "2.3.5"

property theAccessoryView : missing value
property accessoryViewResults : missing value

property theTop : 0
property theTableData : {}
property selectedViews : {}
property lastPosition : {}
property whichViews : {}
property viewPath : {}
set theTableData to {¬
	{"create image view", 40}, ¬
	{"create label", 17}, ¬
	{"create field", 23}, ¬
	{"create side labeled field", 22}, ¬
	{"create top labeled field", 47}, ¬
	{"create checkbox", 18}, ¬
	{"create labeled checkbox", 18}, ¬
	{"create matrix", 58}, ¬
	{"create labeled matrix", 58}, ¬
	{"create path control", 26}, ¬
	{"create labeled path control", 42}, ¬
	{"create popup", 26}, ¬
	{"create labeled popup", 26}, ¬
	{"create secure field", 22}, ¬
	{"create rule", 1} ¬
		}
PersistentVariables()

repeat
	set accessoryView to AssembleAccessoryViews(whichViews)
	set whichViews to DisplayTheTable(theTableData, theAccessoryView)
	StorePersistentValues()
end repeat

on DisplayTheTable(theTableData, the accessoryView)
	local tableResult
	if accessoryViewResults is not {} then
		set middleButtonName to "Display Results"
	else
		set middleButtonName to missing value
	end if
	set tablePrompt to ("Select the Accessory Views you want to see" & return & "Last top: " & theTop as text)
	set theTable to make new table with data theTableData ¬
		empty selection allowed true ¬
		with title ("Accessory View Demo") ¬
		with prompt tablePrompt ¬
		multiple selections allowed true ¬
		can add and delete true ¬
		editable columns {} ¬
		column headings {"Accessory View", "View Height"} ¬
		row numbering false ¬
		initially selected rows selectedViews ¬
		with double click means OK
	
	modify table theTable ¬
		OK button name ("Continue") ¬
		OK button is default true ¬
		extra button name middleButtonName ¬
		accessory view theAccessoryView ¬
		initial position lastPosition
	set theTop to 0
	set theAccessoryView to missing value -- to avoid error messages when saving
	set tableResult to display table theTable ¬
		with extended results
	tell tableResult
		set selectedValues to its values selected
		set selectedViews to its rows selected
		set lastPosition to items 1 thru 2 of its final position
		set whichButton to its button number
	end tell
	
	if accessoryViewResults is not {} and whichButton is 2 then
		set controlResults to {}
		
		repeat with thisAccessoryView in accessoryViewResults
			set {viewType, aControl} to thisAccessoryView as item
			if (aControl's isKindOfClass:(current application's NSTextField)) as boolean then
				
				set end of controlResults to {viewType, viewType & "'s stringValue()", aControl's stringValue() as text}
			else if (aControl's isKindOfClass:(current application's NSPopUpButton)) as boolean then
				set end of controlResults to {viewType, viewType & "'s titleOfSelectedItem()", aControl's titleOfSelectedItem() as text}
			else if (aControl's isKindOfClass:(current application's NSButton)) as boolean then
				set thisResult to aControl's state() as boolean
				if thisResult then
					set thisResult to "true" as text
				else
					set thisResult to "false" as text
				end if
				set end of controlResults to {viewType, viewType & "'s  state()", thisResult}
				
			else if (aControl's isKindOfClass:(current application's NSPathControl)) as boolean then
				set end of controlResults to {viewType, viewType & "'s  |URL|()'s |path|()", aControl's |URL|()'s |path|() as text}
			else if (aControl's isKindOfClass:(current application's NSMatrix)) as boolean then
				set end of controlResults to {viewType, viewType & "'s  title()", aControl's selectedCell()'s title() as text}
			else if (aControl's isKindOfClass:(current application's NSImageView)) as boolean then
				set end of controlResults to {viewType, viewType & "'s image()'s |name|()", aControl's image()'s |name|() as text}
			else -- NSBox 
				set end of controlResults to missing value
			end if
		end repeat
		
		--DisplayTableWithData
		set tableData to controlResults
		set tableTitle to ("Accessory View Results")
		set tablePrompt to ("These are the values of the accessory views shown")
		set multipleLinesAllowed to true --trueOrFalse ¬
		set multipleSelectionsAllowed to true --trueOrFalse
		set emptySelectionAllowed to true --trueOrFalse
		set canAddAndDelete to false --trueOrFalse
		set rowNumbering to true --trueOrFalse
		set editableColumns to {}
		set columnHeadings to {"View Variable", "View Ref", "TextValue"}
		set initiallySelectedRows to {}
		set doubleClickable to true --trueOrFalse
		
		try
			
			set tableResult to display table with data tableData ¬
				with title tableTitle ¬
				with prompt tablePrompt ¬
				multiple lines allowed multipleLinesAllowed ¬
				multiple selections allowed multipleSelectionsAllowed ¬
				empty selection allowed emptySelectionAllowed ¬
				can add and delete canAddAndDelete ¬
				row numbering rowNumbering ¬
				editable columns editableColumns ¬
				column headings columnHeadings ¬
				initially selected rows initiallySelectedRows ¬
				double click means OK doubleClickable
		on error errMsg number errNum
			errMsg
		end try
		
		set buttonNumber to button number of tableResult
		set finalPosition to final position of tableResult
		set selectedRows to rows selected of tableResult
		set timedOut to timed out of tableResult
		set valuesReturned to values returned of tableResult
		set resultsSelected to values selected of tableResult
	end if
	
	
	return selectedValues
end DisplayTheTable

on AssembleAccessoryViews(whichViews)
	local viewsList, accViewControls, AccViewLeft, AccViewBottom, AccViewWidth, AccViewTop, spacer
	set accessoryViewResults to {}
	set viewsList to {}
	repeat with thisView in whichViews
		set the end of viewsList to item 1 of thisView as text
	end repeat
	set accViewControls to {}
	set AccViewLeft to 1
	set AccViewBottom to 0
	set AccViewWidth to 300
	set AccViewTop to 250
	
	set theBottom to AccViewBottom
	set theLeft to AccViewLeft
	set spacer to 1
	set maxWidth to AccViewWidth
	
	--create rule 
	if "create rule" is in viewsList then
		set {rule1, theTop} to create rule theBottom ¬
			left inset AccViewLeft ¬
			rule width AccViewWidth
		set the end of accViewControls to rule1
		set theBottom to theTop + spacer
	end if
	
	--create field
	if "create field" is in viewsList then
		
		set enteredText to "Entered Text"
		set defaultText to "Default Text"
		set theWidth to maxWidth
		set extraHeight to spacer
		set acceptsTabs to true
		set fieldLeft to AccViewLeft
		set {theField, theTop} to create field enteredText ¬
			placeholder text defaultText ¬
			left inset fieldLeft ¬
			bottom theBottom ¬
			field width theWidth ¬
			extra height extraHeight ¬
			with accepts linebreak and tab
		set theBottom to theTop + spacer
		set the end of accViewControls to theField
		set the end of accessoryViewResults to {"theField", theField}
		
	end if
	
	--create image view
	if "create image view" is in viewsList then
		set posixPath to POSIX path of viewPath
		set viewWidth to 40
		set viewHeight to 40
		set imageScale to scale to fit
		--   scales:    scale down proportionally, scale to fit, scale none, scale proportionally}
		set imageAlignment to bottom left aligned
		--   imageAlignments:   center aligned, top aligned, top left aligned, top right aligned
		--               left aligned, bottom left aligned, bottom right aligned, right aligned, bottom aligned
		
		set {theImage, theTop} to create image view posixPath ¬
			left inset AccViewLeft ¬
			bottom theBottom ¬
			view width viewWidth ¬
			view height viewHeight ¬
			scale image imageScale ¬
			align image imageAlignment
		set theBottom to theTop + spacer
		set the end of accViewControls to theImage
		set the end of accessoryViewResults to {"theImage", theImage}
		
	end if
	
	--create label 
	if "create label" is in viewsList then
		set maxWidth to 280
		set wrapsBool to false
		set boldType to true
		set labelString to "LabelString:"
		set LabelLeft to 0
		set alignment to left aligned
		--    alignments:    left aligned, right aligned or center aligned
		set controlSize to regular size
		--   control sizes:   regular size,  mini size, small size,large size 
		
		set labelWidth to max width for labels {labelString} ¬
			control size controlSize ¬
			bold type boldType
		set labelDepth to max depth for label labelString ¬
			max width labelWidth ¬
			control size controlSize ¬
			bold type boldType
		
		set {theLabel, theTop} to create label labelString ¬
			left inset LabelLeft ¬
			bottom theBottom ¬
			max width maxWidth ¬
			aligns alignment ¬
			multiline wrapsBool ¬
			control size controlSize ¬
			bold type boldType
		set theBottom to theTop + spacer
		set the end of accViewControls to theLabel
	end if
	
	--create labeled matrix
	if "create labeled matrix" is in viewsList then
		-- makes label and matrix; if matrixLeft is less than leftInset, the label will start at leftInset, with the matrix following;
		-- if matrixLeft is more than leftInset, the matrix will start at matrixLeft, and the label will will appear to its left
		set radioButtonNames to {"Button 1", "Button two", "Button button button"}
		set labelText to "Matrix Label"
		set matrixLabelWidth to (max width for labels {labelText}) + AccViewLeft
		
		set {labeledMatrix, matrixLabel, theTop, matrixLeft} to ¬
			create labeled matrix radioButtonNames ¬
				left inset AccViewLeft ¬
				bottom theBottom ¬
				max width maxWidth ¬
				arranged vertically true ¬
				matrix left matrixLabelWidth ¬
				label text labelText ¬
				initial choice (some item of radioButtonNames)
		set theBottom to theTop + spacer
		set the end of accViewControls to labeledMatrix
		set the end of accViewControls to matrixLabel
		set the end of accessoryViewResults to {"labeledMatrix", labeledMatrix}
		
	end if
	
	--create labeled path control 
	if "create labeled path control" is in viewsList then
		set labelText to "Path control label"
		set pathControlLabelWidth to max width for labels {labelText}
		set myPath to POSIX path of (path to me)
		
		set {labeledPathControl, pathControlLabel, theTop} to create labeled path control myPath ¬
			left inset AccViewLeft ¬
			bottom theBottom ¬
			control width maxWidth ¬
			pops up (some item of {true, false}) ¬
			label text labelText
		
		
		
		set theBottom to theTop + spacer
		set the end of accViewControls to labeledPathControl
		set the end of accViewControls to pathControlLabel
		set the end of accessoryViewResults to {"labeledPathControl", labeledPathControl}
	end if
	
	--create create labeled popup
	if "create labeled popup" is in viewsList then
		set labelText to "Popup label"
		set popupLabelWidth to max width for labels {labelText}
		set popupList to {"Fred", "Frieda", "George", "Georgina"}
		
		set {theLabeledPopup, thePopupLabel, theTop, theWidth} to ¬
			create labeled popup popupList ¬
				left inset AccViewLeft ¬
				bottom theTop ¬
				popup width maxWidth - popupLabelWidth ¬
				max width maxWidth ¬
				label text labelText ¬
				popup left popupLabelWidth ¬
				initial choice (some item of popupList)
		
		
		
		set theBottom to theTop + spacer
		set the end of accViewControls to thePopupLabel
		set the end of accViewControls to theLabeledPopup
		set the end of accessoryViewResults to {"theLabeledPopup", theLabeledPopup}
	end if
	
	--create matrix
	if "create matrix" is in viewsList then
		
		set radioButtonNames to {"Button 1", "Button two", "Button button button"}
		
		set {theMatrix, theTop} to create matrix radioButtonNames ¬
			left inset AccViewLeft ¬
			bottom theBottom ¬
			max width maxWidth ¬
			arranged vertically true ¬
			initial choice some item of radioButtonNames
		set theBottom to theTop + spacer
		set the end of accViewControls to theMatrix
		set the end of accessoryViewResults to {"theMatrix", theMatrix}
	end if
	
	--create path control  
	if "create path control" is in viewsList then
		set myPath to POSIX path of (path to me)
		
		set {thePathControl, theTop} to create path control myPath ¬
			left inset AccViewLeft ¬
			bottom theBottom ¬
			control width maxWidth ¬
			with pops up
		set theBottom to theTop + spacer
		set the end of accViewControls to thePathControl
		set the end of accessoryViewResults to {"thePathControl", thePathControl}
	end if
	
	--create popup 
	if "create popup" is in viewsList then
		set popupList to {"Fred", "Frieda", "George", "Georgina"}
		set {thePopup, theTop} to create popup popupList ¬
			left inset AccViewLeft ¬
			bottom theBottom ¬
			popup width maxWidth ¬
			initial choice (some item of popupList)
		set theBottom to theTop + spacer
		set the end of accViewControls to thePopup
		set the end of accessoryViewResults to {"thePopup", thePopup}
	end if
	
	--create checkbox  
	if "create checkbox" is in viewsList then
		set checkBoxTitle to "Check Box Title"
		set {theCheckbox, theTop} to create checkbox checkBoxTitle ¬
			left inset theLeft ¬
			bottom theBottom ¬
			max width maxWidth ¬
			with initial state
		set theBottom to theTop + spacer
		set the end of accViewControls to theCheckbox
		set the end of accessoryViewResults to {"theCheckbox", theCheckbox}
	end if
	
	--create labeled checkbox
	if "create labeled checkbox" is in viewsList then
		-- makes label and checkbox; if checkboxLeft is less than leftInset, the label will start at leftInset, with the checkbox following;
		-- if checkboxLeft is more than leftInset, the checkbox will start at checkboxLeft, and the label will will appear to its left
		
		set ceckboxLabel to "Label for checkbox"
		set checkboxLeft to AccViewLeft
		set checkBoxLabelText to "Text for Checkbox"
		set {theLabeledCheckBox, theCheckBoxLabel, theTop, theLeft} to ¬
			create labeled checkbox ceckboxLabel ¬
				left inset AccViewLeft ¬
				bottom theBottom ¬
				max width maxWidth ¬
				label text checkBoxLabelText ¬
				checkbox left checkboxLeft ¬
				without initial state
		set theBottom to theTop + spacer
		set the end of accViewControls to theCheckBoxLabel
		set the end of accViewControls to theLabeledCheckBox
		set the end of accessoryViewResults to {"theLabeledCheckBox", theLabeledCheckBox}
	end if
	
	--create secure field 
	if "create secure field" is in viewsList then
		
		set secureFieldText to "Secure Field"
		set secureFieldPlaceHolder to "Secure Placeholder"
		
		set {theSecureField, theTop} to create secure field secureFieldText ¬
			placeholder text secureFieldPlaceHolder ¬
			left inset AccViewLeft ¬
			bottom theTop ¬
			field width maxWidth
		set theBottom to theTop + spacer
		set the end of accViewControls to theSecureField
		set the end of accessoryViewResults to {"theSecureField", theSecureField}
	end if
	
	--create side labeled field 
	if "create side labeled field" is in viewsList then
		set labelText to "field label"
		set labelWidth to max width for labels {labelText}
		set fieldContents to "Side label field contents"
		set placeHolderText to "Side label field placeholder"
		
		set {theSideLabeledField, theFieldLabel, theTop, theWidth} ¬
			to create side labeled field fieldContents ¬
			placeholder text placeHolderText ¬
			left inset AccViewLeft ¬
			bottom theTop ¬
			total width maxWidth ¬
			accepts linebreak and tab true ¬
			label text labelText ¬
			field left AccViewLeft + labelWidth
		
		set theBottom to theTop + spacer
		set the end of accViewControls to theFieldLabel
		set the end of accViewControls to theSideLabeledField
		set the end of accessoryViewResults to {"theSideLabeledField", theSideLabeledField}
	end if
	
	--create top labeled field 
	if "create top labeled field" is in viewsList then
		set labelText to "field label"
		set labelWidth to max width for labels {labelText}
		set fieldContents to "Top label field contents"
		set placeHolderText to "top label field placeholder"
		
		set {theTopLabeledField, theFieldLabel, theTop} to create top labeled field fieldContents ¬
			placeholder text placeHolderText ¬
			left inset AccViewLeft ¬
			bottom theTop ¬
			field width maxWidth ¬
			extra height spacer ¬
			accepts linebreak and tab true ¬
			label text labelText
		
		
		set theBottom to theTop + spacer
		set the end of accViewControls to theTopLabeledField
		set the end of accViewControls to theFieldLabel
		set the end of accessoryViewResults to {"theTopLabeledField", theTopLabeledField}
		
	end if
	
	if theTop > 250 then
		set AccViewTop to 250
	else
		set AccViewTop to theTop
	end if
	
	my AssembleAccessoryViewsMainThread:{accViewControls, AccViewLeft, AccViewBottom, AccViewWidth, AccViewTop}
end AssembleAccessoryViews

on AssembleAccessoryViewsMainThread:theArg
	set {accViewControls, theLeft, theBottom, theWidth, theTop} to theArg
	set my theAccessoryView to current application's NSView's alloc()'s initWithFrame:{{theLeft, theBottom}, {theWidth, theTop - theBottom}}
	repeat with aControl in accViewControls
		(theAccessoryView's addSubview:aControl)
		
	end repeat
	
	
	
end AssembleAccessoryViewsMainThread:

on PersistentVariables()
	set newView to false
	prepare storage for (path to me) default values {selectedViews:{}, lastPosition:{}, whichViews:{}, viewPath:{}}
	
	set my selectedViews to value for key "selectedViews"
	set my lastPosition to value for key "lastPosition"
	set my whichViews to value for key "whichViews"
	set my viewPath to value for key "viewPath"
	
	if selectedViews = {} then
		set selectedViews to {1, 2, 3}
		assign value selectedViews to key "selectedViews"
	end if
	
	if lastPosition = {} then
		set lastPosition to {30, 30}
		assign value lastPosition to key "lastPosition"
	end if
	
	if whichViews = {} then
		set whichViews to items 1 thru 3 of theTableData
		assign value whichViews to key "whichViews"
	end if
	
	if viewPath = {} then
		set newView to true
	else
		try
			if not (exists object viewPath) then set newView to true
		on error
			set newView to true
		end try
	end if
	
	if newView then
		set viewPath to choose file with prompt ("Select an image for image view") ¬
			of type {"jpg", "jpeg", "tif", "tiff"} ¬
			invisibles false ¬
			multiple selections allowed false ¬
			without showing package contents
		set viewPath to POSIX path of viewPath
		assign value viewPath to key "viewPath"
	end if
end PersistentVariables

on StorePersistentValues()
	assign value selectedViews to key "selectedViews"
	assign value lastPosition to key "lastPosition"
	assign value whichViews to key "whichViews"
	assign value viewPath to key "viewPath"
end StorePersistentValues

on RetreiveStoredValues()
	set my selectedViews to value for key "selectedViews"
	set my lastPosition to value for key "lastPosition"
	set my whichViews to value for key "whichViews"
	set my viewPath to value for key "viewPath"
end RetreiveStoredValues



Edit: Better result Reporting

1 Like

Received the following error when I tried to execute it:

error “The file passed to the ‘prepare storage’ command is not a valid applet or script bundle” number -2700 from «script “PrefsStorageLib”» to item

Model: iMac
AppleScript: 2.7
Browser: Safari 537.36
Operating System: macOS 10.12

I should have mentioned it needs to be saved as a bundle or an app before you run it.

I’ll add that to the OP