Got it centered! Thanks again.
What did you do to center it?
Can you share your full script here?
If you take a look at the two images I’ve uploaded, take a look at the “aView’s setString:(” location. If you put the cursor at the far right " mark. and step backwards. there are 15 spaces in the original, which when changed to 12 spaces, centers the numbers. I’ve included the modified script.
use scripting additions
use framework "Foundation"
use framework "AppKit"
property wController : missing value -- outlet equivalent in AsObjC
property notificationTitle : "Restarting... "
property aWidth : 360
property aHeight : 30
my performSelectorOnMainThread:"displayNotification:" withObject:({aWidth, aHeight, notificationTitle}) waitUntilDone:true
tell application "System Events" to restart with state saving preference
------------------------------------ HANDLERS ------------------------------------------------
on displayNotification:paramObj
copy paramObj 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 aView to current application's NSTextView's alloc()'s initWithFrame:(current application's NSMakeRect(0, 0, aWidth, aHeight))
aView's setRichText:true
aView's useAllLigatures:true
aView's setTextColor:(current application's NSColor's cyanColor()) --
aView's setBackgroundColor:aColor
aView's setEditable:false
aView's setFont:(current application's NSFont's fontWithName:"Menlo" |size|:20)
repeat with i from 15 to 1 by -1
set my wController to current application's NSWindowController's alloc()
(aView's setString:(" " & i & " sec"))
set aWin to makeWinWithView(aView, aWidth, aHeight, aTitle, 0.9)
my (wController's initWithWindow:aWin)
my (wController's showWindow:me)
delay 1
my wController's |close|()
end repeat
end displayNotification:
on makeWinWithView(aView, aWinWidth, aWinHeight, aTitle, alphaV)
set aScreen to current application's NSScreen's mainScreen()
set aFrame to {{0, 0}, {aWinWidth, aWinHeight}}
set aBacking to current application's NSTitledWindowMask
set aDefer to current application's NSBackingStoreBuffered
set aWin to current application's NSWindow's alloc()
(aWin's initWithContentRect:aFrame styleMask:aBacking backing:aDefer defer:false screen:aScreen)
aWin's setTitle:notificationTitle
aWin's setDelegate:me
aWin's setDisplaysWhenScreenProfileChanges:true
aWin's setHasShadow:true
aWin's setIgnoresMouseEvents:false
aWin's setLevel:(current application's NSNormalWindowLevel)
aWin's setOpaque:false
aWin's setAlphaValue:alphaV --append
aWin's setReleasedWhenClosed:true
aWin's |center|()
aWin's setContentView:aView
return aWin
end makeWinWithView```
Is it possible to add either a “Cancel” or “Stop” button to this script?
I know this an old thread, but I have been trying to add a cancel button to both the Shut Down and Restart scripts, with absolutely no success. Any help would be appreciated.
This is quite difficult because you have to add the NSButton
to the attached NSView
in the AppleScriptObjC part and handle the mouse events.
Apart from that it likely won’t work in your environment because you create new windows/views in each iteration of the repeat loop and close them again which is not a good practice.
Thank you for the explanation. So, would it be possible if the window was static, and only showed 5 seconds until either shut down or restart?
Not looking to make work for you, just wondering if there might be a solution.
It is even possible with your code but catching the mouse events might be unreliable because a new window is created and destroyed every second.
I may have found a perfectly acceptable solution. I used Peavine’s script in post #5, and it works well for both restart and shut down.
Restart:
display dialog "The computer will restart in 15 seconds." buttons {"Cancel", "Restart Now"} default button 1 giving up after 15
tell application "System Events" to restart with state saving preference
Shut Down:
display dialog "The computer will shut down in 15 seconds." buttons {"Cancel", "Shut Down Now"} default button 1 giving up after 15
tell application "System Events" to shut down with state saving preference
As mentioned, your script can be adjusted, since I think it looks better than the vanilla dialog. The main things are to get rid of recreating the window and just change the textView, add a cancel button, and manually process events so that you can actually press the button while in the repeat loop.
Depending on how the main script is split up, another option would be to use osascript
to run a script using the system ScriptMonitor application. That puts a status item with a gear icon in the menubar that includes a cancel button for each script/workflow it watches - from my testing, that will pretty much kill a script no matter what it is doing (or not doing).
I did work up a version of the ASObjC script that uses an accessory view to place a button (one of those small round ones with an X
) on the right side of the window’s title bar. The sample script is about twice the size, which is to say way bigger than a single display dialog
statement, if you still want to see that.
Why not simply taking advantage of the progress view skills of AppleScript, save the script as application and uncomment the System Events
line at the end.
set theCounter to 15
set progressCounter to 0
set progress total steps to theCounter
set progress completed steps to progressCounter
set progress additional description to ""
repeat with a from theCounter to 1 by -1
set progress description to "The computer will shut down in " & a & " seconds."
set progress completed steps to progressCounter
set progressCounter to progressCounter + 1
delay 1
end repeat
-- Reset the progress information
set progress total steps to 0
set progress completed steps to 0
set progress description to ""
set progress additional description to ""
-- tell application "System Events" to shut down with state saving preference
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.
is not an app, it’s a compiled 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
Another option with swiftDialog is:
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
The last line of the housekeeping script is in fact correct, unless you were referring to something else.
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.
I placed that in the dialog in the run
handler to avoid any accidents, so just replace the display dialog
statement with your shutdown code.
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.