StefanK, I copied your script into Script Debugger, then turned it into an app, and when double clicked the window comes up on the screen and the countdown begins, perfect.
But, I have a script (turned into an app as well) that does some additional housekeeping prior to either a shut down or restart. I’ve included that script below. Your script is on my Desktop named Shut Down.scpt. Problem is, that when that my script is run (with your Shut Down.scpt included), the window no longer comes up on the screen. Any ideas?
use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
delay 1
run script "Users:homer:Documents:MacBook Pro Specific:AppleScript Files:Clear Clipy.scpt" as alias
delay 1
run script "Users:homer:Documents:MacBook Pro Specific:AppleScript Files:Clear Recent Menus.scpt" as alias
delay 1
run script "Users:homer:Desktop:Shut Down.scpt" as alias
Would love to see that script. But, see my response to StafanK below, as whatever your script is, it would need to be that last item in my housekeeping shut down or restart script.
The following is an extended version of the script by @KniazidisR that doesn’t put up multiple windows and includes a small cancel button in the toolbar. To prevent the UI from being blocked while in the repeat loop, a handler is used to manually process events (the cancel button works, no beachball of death, etc). Since the script also takes care of its own window, it works the same as an application and via run script and osascript.
use framework "Foundation"
use scripting additions
property notificationTitle : "The computer will shut down in 15 seconds... " -- pad as desired for alignment
property aWidth : 360
property aHeight : 30
global cancelled -- a flag for the cancel button, since `performSelectorOnMainThread` doesn't return anything
on run
set cancelled to false
my performSelectorOnMainThread:"displayNotification:" withObject:{aWidth, aHeight, notificationTitle} waitUntilDone:true
activate me
if cancelled then -- indicate cancellation
display alert "Shutdown cancelled" message "The system shutown has been cancelled." giving up after 3
else -- do whatever
display dialog "tell application \"System Events\" to shut down with state saving preference" with title "Dry Run" buttons {"OK"} default button 1
end if
end run
on displayNotification:parameterList -- main handler
copy (parameterList as list) to {aWidth, aHeight, aTitle}
set aColor to current application's NSColor's colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:0.9
set aWin to makeWindow(aWidth, aHeight, aTitle, 0.9)
set aView to makeTextView(aWidth, aHeight, aColor)
aWin's setContentView:aView
aWin's makeKeyAndOrderFront:me
repeat with i from 15 to 1 by -1
(aView's setString:("" & i & " sec"))
delay 1
processEvents for any -- don't block UI
if cancelled then exit repeat
end repeat
aWin's |close|()
end displayNotification:
to makeTextView(width, height, background) -- make a countdown textView for the window content
tell (current application's NSTextView's alloc()'s initWithFrame:{{0, 0}, {width, height}})
its setTextColor:(current application's NSColor's cyanColor())
its setBackgroundColor:background
its setFont:(current application's NSFont's fontWithName:"Menlo" |size|:18)
its setAlignment:(current application's NSTextAlignmentCenter)
repeat 3 times -- tweak vertical alignment
its lowerBaseline:me
end repeat
its setEditable:false -- no insertion point
return it
end tell
end makeTextView
to makeWindow(width, height, title, alpha) -- make a window with a titlebar accessory
tell (current application's NSWindow's alloc()'s initWithContentRect:{{0, 0}, {width, height}} styleMask:1 backing:2 defer:true screen:(current application's NSScreen's mainScreen()))
its |center|()
its setDisplaysWhenScreenProfileChanges:true
its setAlphaValue:alpha
its setReleasedWhenClosed:true
its setLevel:(current application's NSNormalWindowLevel) -- NSFloatingWindowLevel
its setTitle:title
its addTitlebarAccessoryViewController:(my makeTitleBar())
return it
end tell
end makeWindow
to makeTitleBar() -- make the titlebar accessory
set titlebarView to current application's NSView's alloc()'s initWithFrame:{{0, 0}, {30, 30}}
titlebarView's addSubview:(makeCancelButton(0, 4)) -- tweak to center vertically
tell current application's NSTitlebarAccessoryViewController's alloc()'s init()
its setAutomaticallyAdjustsSize:true
its setView:titlebarView
its setLayoutAttribute:(current application's NSLayoutAttributeRight)
return it
end tell
end makeTitleBar
to makeCancelButton(x, y) -- make a cancel button for the titlebar accessory
tell (current application's NSButton's alloc's initWithFrame:{{x, y}, {20, 20}})
its setButtonType:(current application's NSMomentaryPushInButton)
its setBezelStyle:(current application's NSCircularBezelStyle)
its setBordered:false
its setImageScaling:(current application's NSImageScaleProportionallyUpOrDown)
its setImage:(current application's NSImage's imageNamed:(current application's NSImageNameStopProgressFreestandingTemplate))
its setTarget:me
its setAction:"cancelAction:"
its setKeyEquivalent:(character id 27) -- escape key
its setRefusesFirstResponder:true -- no focus ring
return it
end tell
end makeCancelButton
on cancelAction:sender -- set a flag indicating the cancel button was pressed
set cancelled to true
end cancelAction:
to processEvents for eventMask given mode:runLoopMode : (missing value) -- manually handle events to avoid blocking the UI
if eventMask is any then set eventMask to a reference to current application's NSEventMaskAny
if runLoopMode is missing value then set runLoopMode to current application's NSDefaultRunLoopMode -- NSModalPanelRunLoopMode
repeat -- forever
tell current application's NSApp to set theEvent to its nextEventMatchingMask:eventMask untilDate:(missing value) inMode:runLoopMode dequeue:true
if theEvent is missing value then exit repeat -- none left
tell current application's NSApp to sendEvent:theEvent
end repeat
end processEvents
FWIW, another option is the free swiftDialog app (here). One issue with this script is that the keyboard cancel key does not cancel the dialog. You instead have to press the Enter key or select the Stop button with the mouse.
As written, the dialog only shows a message with the count-down text, but it can be changed with icons, markdown text, and many other enhancements.
set dialogPID to do shell script "/usr/local/bin/dialog --title none --message 'The computer will shut down in 10 seconds' --messagefont 'size=14' --width 325 --height 75 --messageposition bottom --button1text 'Stop' --hideicon &> /dev/null & echo $!"
repeat with i from 9 to 0 by -1 --update dialog content
delay 1
if dialogExists(dialogPID) is false then error number -128 --stop script if dialog not found
do shell script "/bin/echo 'message: The computer will shut down in " & i & " seconds' >> /var/tmp/dialog.log"
end repeat
do shell script "/bin/echo 'quit: true' >> /var/tmp/dialog.log" --quit dialog
--tell application "System Events" to shut down with state saving preference
on dialogExists(dialogPID)
try
do shell script "ps -p " & dialogPID & " > /dev/null"
return true
end try
return false
end dialogExists
set dialogResult to do shell script "/usr/local/bin/dialog --title none --message 'The computer will sleep in 10 seconds' --messagefont 'size=14' --button1text 'Sleep' --button2text 'Cancel' --width 350 --height 90 --hideicon --timer 10 --messageposition bottom; echo $?"
if dialogResult is "2" then error number -128
tell application "System Events" to sleep
That is one impressive script. At the very end a window pops up, and I tried to figure out where to put the “tell application “System Events” to shut down with state saving preference”. I must have tried at least a half dozen locations, with no success. I’m sure you have my solution, thank you.
Works like a charm, thank you very much. My next attempt will be to turn your shut down script into a restart script, I’m sure I’ll manage. Thanks again!
Edit: That didn’t take long, I have both your shut down and restart scripts working well with my housekeeping script.