Would like to figure out how to write a Date Picker script to use the Cocoa built-in date picker.
If anyone could do it, Shane could. (Kissing butt here!)
I wrote it 6 years ago.
http://piyocast.com/as/archives/5571
And…I packed it into “choose date lib” AppleScript libraries with sdef.
http://piyocast.com/as/archives/7713
But Calendar picker is too small for latest Mac environment. So, I wrote some larger calendar pickers.
I wrote one in Swift
Piyomaru,
Well, your lib is run-only and locked so we can’t see the AppleScript.
Also it only returns a list with 3 items representing the date but no time element.
It should return a date/time
I think this is a simple subject that is perfect for you to tackle yourself.
I tried downloading and running this script.
I get an error
“Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.”
it fails on this line
set theSize to datePicker’s fittingSize()
You have to know about execute handler in main thread.
Some of Cocoa API require to run in main thread.
Apple’s Script Editor has Control-Command-R to execute whole AppleScript to run in main thread.
Script Debugger does not have this function.
I wrote a lot of this sort of code.
Shane answered a lot of time about this.
You have to find them by yourself.
We are not your caregivers.
I Got it working
-- Created 2019-02-14 by Takaaki Naganoya
-- 2019 Piyomaru Software, parts 2025 RMF Solutions
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"
property NSAlert : a reference to current application's NSAlert
property NSDate : a reference to current application's NSDate
property NSView : a reference to current application's NSView
property NSDatePicker : a reference to current application's NSDatePicker
property NSRunningApplication : a reference to current application's NSRunningApplication
property theResult : missing value
property returnCode : 0
property cancelled : false
on pickDate(msg)
if msg = "" then set msg to "Pick Date"
set paramObj to {myMessage:msg, mySubMessage:"Choose a date and time…"}
if my NSThread's isMainThread() as boolean then
my chooseDate:paramObj
else
my performSelectorOnMainThread:"chooseDate:" withObject:paramObj waitUntilDone:true
end if
if cancelled then return false
return theResult
end pickDate
on chooseDate:paramObj
local aRect, aMainMes, theView, aSubMes, datePicker, theSize
set aRect to current application's NSMakeRect(0, 0, 280, 280)
set aMainMes to myMessage of paramObj as text
set aSubMes to mySubMessage of paramObj as text
-- create a view
set theView to NSView's alloc()'s initWithFrame:aRect --(current application's NSMakeRect(0, 0, 280, 280))
set datePicker to NSDatePicker's alloc()'s initWithFrame:aRect --(current application's NSMakeRect(0, 0, 280, 280))
--datePicker's setDatePickerStyle:(NSClockAndCalendarDatePickerStyle)
datePicker's setDatePickerStyle:(current application's NSDatePickerStyleClockAndCalendar)
datePicker's setDatePickerElements:((current application's NSYearMonthDayDatePickerElementFlag) + (current application's NSHourMinuteSecondDatePickerElementFlag as integer))
--datePicker's setDatePickerElements:((NSYearMonthDayDatePickerElementFlag) + (NSHourMinuteSecondDatePickerElementFlag as integer))
datePicker's setDateValue:(NSDate's |date|())
--set theSize to datePicker's fittingSize()
set theSize to {280, 170}
theView's setFrameSize:theSize
datePicker's setFrameSize:theSize
theView's setSubviews:{datePicker}
set theAlert to NSAlert's alloc()'s init()
-- set up alert
tell theAlert
its setMessageText:aMainMes
its setInformativeText:aSubMes
its addButtonWithTitle:"OK"
its addButtonWithTitle:"Cancel"
its setAccessoryView:theView
end tell
-- show alert in modal loop
NSRunningApplication's currentApplication()'s activateWithOptions:0
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
set my cancelled to (returnCode = 1001) --error number -128
set (my theResult) to (datePicker's dateValue()) as date
end chooseDate:
on doModal:aParam
set (my returnCode) to aParam's runModal()
end doModal:
Save this in your Script Libraries location
Use the command
set myDate to pickDate("Please Pick a Date and Time…") of script "DatePicker"
Hi, I was trying to understand the logic in the script.
in handler pickDate(msg), if it is not running on the main thread, then “chooseDate” is called on MainThread by “my performSelectorOnMainThread:“chooseDate:” withObject:paramObj waitUntilDone:true”.
so is it necessary in handler “chooseDate:paramObj”, to call my performSelectorOnMainThread:“doModal:” withObject:(theAlert) waitUntilDone:true ? i thought it’s already on the main thread
is it true what I am thinking. Thank you
Detecting main thread execution is not necessary.
But I usually write both main thread execution and non-main thread execution calling code.
Non-main thread execution is for debugging.
“performSelectorOnMainThread:” does not detect error point.
We can not know about the wrong point in main threaded execution.
So, in debugging phase, I use Script Editor and run AppleScript (non-main thread call) with Command-Control-R.
We can find out the wrong point.
http://piyocast.com/as/archives/10143
–my displayAlertDialog:paramObj
This is it. I usually delete non-main threaded calling, Sometimes a trace of debugging remain.
Also note that although you can’t get rid of the standard NSAlert views, you can alter them (change fonts and colors, etc). For example, hide the icon and text fields and cover the space with the datePicker view. Add a label textField as an accessory view, and it almost looks like a custom dialog (alignments tested with Monterey, Sonoma, etc. - the iOS-ified alert layout):
use framework "Foundation"
use scripting additions
property response : missing value -- AppleScript date object - note that `performSelectorOnMainThread` doesn't return anything
on run -- testing in a script editor, the Script Menu, etc
set optionRecord to {} -- just use defaults
my performSelectorOnMainThread:"chooseDate:" withObject:optionRecord waitUntilDone:true
# do whatever with the response
if response is missing value then error number -128
return response -- or whatever date pieces
end run
# Set response property from NSAlert result.
to chooseDate:options
set options to (options as record) & {prompt:"Choose a date and time…", ISO:false} -- merge options with defaults
tell current application's NSAlert's alloc()'s init()
repeat with aView in {item 4, item 5, item 6} of its |window|'s contentView's subviews -- remove item 4 for alignment
(aView's setHidden:true) -- hide icon imageView, message textField, and informative textField
end repeat
set datePicker to my makeDatePicker()
its (|window|'s contentView's addSubview:(datePicker))
its setMessageText:(return & return & return) -- push down the informative textField to make room for added label
its setInformativeText:""
repeat with button in {"OK", "Cancel"} -- just two to avoid stacking the buttons
(its addButtonWithTitle:button)
end repeat
its setAccessoryView:(my (makeLabelField for (prompt of options))) -- below the (hidden) informative textField
set reply to (its runModal())
end tell
if reply is (current application's NSAlertSecondButtonReturn) as integer then
set my response to missing value
else -- just return the entire ISO date string or AppleScript date and let the caller take it from there
set theDate to (datePicker's dateValue()) as date
set my response to item ((ISO of options as integer) + 1) of {theDate, theDate as «class isot» as string}
end if
end chooseDate:
# Make and return a date picker view to cover the regular NSAlert views.
to makeDatePicker()
tell (current application's NSDatePicker's alloc()'s initWithFrame:{{17, 95}, {277, 148}})
its setDatePickerStyle:(current application's NSClockAndCalendarDatePickerStyle)
its setDatePickerElements:((current application's NSYearMonthDayDatePickerElementFlag as integer) ¬
+ (current application's NSHourMinuteSecondDatePickerElementFlag as integer))
its setDateValue:(current application's NSDate's |date|())
return it
end tell
end makeDatePicker
# Make and return a label text field for a relocated prompt below the NSDatePicker.
to makeLabelField for (theString as text)
tell (current application's NSTextField's labelWithString:theString)
its setFrame:{{0, 0}, {280, 18}} -- a single line the same width as the datePicker
its setAlignment:(current application's NSTextAlignmentCenter)
its setLineBreakMode:(current application's NSLineBreakByTruncatingMiddle) -- mostly so that any clipping is visible
its setFont:(current application's NSFont's fontWithName:"Helvetica Neue Bold" |size|:12) -- or regular, whatever
# its setBordered:true -- show a border for alignment
return it
end tell
end makeLabelField