try this out and let me know what you think... use Options=
GR=0 ;sets gui radius off
BR=0 ;sets background radius off
BT=255 ;sets background transparency off
BW=1 ;sets thickness to 1 like a tooltip
to make it look like a tooltip, and negative duration kills app again (and is per notify, so a newer notify that doesnt have exit won't kill until the first is clicked or times out... so yo ucan haveone up that says "click me to exit" and others can come and go in the meantime
edit: latest version in original post, this version here for detailed comments
; Notify() Version 0.29 - made by gwarble - sept 09 ; for displaying multiple customizable tray area notifications ; ; Notify([Message,Title,Duration,Options,Image]) ; ; thanks to Rhys and engunneer [Toaster Popups] ; test with [Ctrl-Shift-F12] if Notify.ahk is run ; ;=== To Fix: ;=== fill gaps from removed notifications if fit (might not be worth the wingetpos processing...?, and may not be desireable) ;=== or shift left a "column" width for more than monitor height allows ;=== or collapse open not's on new not or on close not (with delay) ;=== one last global to remove for label name... don't know if its possible ;=== flashing could be improved with options ;=== position could be controlable, could be moveable once open (stickies, reminders, etc) ;=== GN/maxcount need to be made parametric (for now it starts at 11: and allows 40) ;=== flash only works with odd number of flashing not's (easy to fix but may change functionality instead) ;=== have an Options parameter, for color/size/font stuff, position/size stuff, time action, click action, instead of so many parameters, parse internally... ;=== ;==================================================================================== ;==================================================================================== Notify_Demo1: ;=== in case Notify.ahk is run instead of #include-d OptionsList := "GF=11`nGL=30`nGC=FFFFAA`n GR=9`nGT=0`n TS=8`nTW=625`nTC=Black`nTF=Arial`nMS=8`n MW=550`nMC=Black`nMF=Arial`nBC=Black`n BW=2`nBR=9`nBT=105`nBF=150`nSC=300`nSI=250`n ST=100`nAC=`nAT=" Options := "TC=Red MC=Blue GC=Silver" Notify("","Fake Tooltip 1",5,"GR=0 BR=0 BT=255 BW=1") Notify("Fake Tooltip 2") Options := "TC=Red MC=Blue GC=Silver" Notify("Options:",OptionsList,7,Options) Options := "GC=FFFFAA GR=9 GT=Off TS=8 TW=625 TC=Black TF=Arial MS=8 MW=550 MC=Black MF=Arial BC=Black BW=2 BR=9 BT=105 BF=150" Notify("Options:","Default",10,Options) Notify("Notification:","This one won't go away unless clicked!",0) Notify("Notification:","This one won't go away unless clicked!",0,"","Off.ico") Hotkey, ^+F12, Notify_Demo2 Return Notify_Demo2: Notify("Title","Message",2,"GC=CCCC44") Notify("Title","Message",3,"GR=20") Notify("Title","Message",5,"BT=10") Notify("Title","Message",7,"GC=FFFFAA GR=9 GT=Off BT=205 TC=Black MC=Black") Notify() Notify("Title","Message",8,"GR=24 BR=25") Notify("Title","Message",9,"BW=4") Notify("Title","Message",10,"BR=0") Notify("Title","Message",11,"GR=0 MW=550") Notify("Title","Message",12,"BW=1") Notify("Title","Message",13,"MW=550") Notify("Title","Message",15,"SI=1000 SO=1000 SC=500") ;=== slow Hotkey, ^+F12, Notify_Demo3 Return Notify_Demo3: Notify() Notify() Notify() Notify() Notify("Notify()","Exiting in 10`nor if clicked...",-10,"GC=Lime TC=Black MC=White MW=625") Hotkey, ^+F12, Notify_Demo1 Return ;==================================================================================== ;==================================================================================== Notify(Title="Notification!!!",Message="",Duration=15,Options="",Image="") { Static ;=== assume-static mode for arrays of options Critical ;=== don't allow interruption static GF := 11 ;GF is Gui First Number ;=== defaults if no options static GL := 30 ;GM is Gui Last Number ;=== options are saved between calls (ie only set once and changed if in Options) static GC := "FFFFAA" ;GC is Gui Color static GR := 9 ;GR is Gui Radius static GT := "Off" ;GT is Gui Transparency static TS := 8 ;TS is Title Font Size static TW := 625 ;TW is Title Font Weight static TC := "Black" ;TC is Title Font Color static TF := "Arial" ;TF is Title Font static MS := 8 ;MS is Message Font Size static MW := 550 ;MW is Message Font Weight static MC := "Black" ;MC is Message Font Color static MF := "Arial" ;MF is Message Font static BC := "Black" ;BC is Border Colors static BW := 2 ;BW is Border Width/Thickness ;=== hugov, note option (BW BT) change static BR := 9 ;BR is Border Radius static BT := 105 ;BT is Border Transpacency static BF := 150 ;BF is Border Flash Speed static SC := 300 ;SC is Speed Clicked (AnimateWindow time) static SI := 250 ;SI is Speed In (AnimateWindow time) static ST := 100 ;SO is Speed TimeOut (AnimateWindow time) static IW := 32 ;IW is Image Width static IH := 32 ;IH is Image Height Global AC := 0 ;AC is Action Clicked (gosub label when clicked) ;=== Global Notify_Action removed for now Global AT := 0 ;AT is Action Timeout (gosub label when timeout) local ImageShift = "" If (Options) ;=== parse Options parameter Loop,Parse,Options,%A_Space% If (Option:= SubStr(A_LoopField,1,2)) %Option%:= SubStr(A_LoopField,4) ;=== two letter option and = (ie GF=) takes 3 chars, so value starts at 4 (needs to strip quotes if used) GN := GF ;=== GuiNumber[GN] starts at GF[FirstGuiNumber] Loop ;=== max notifications, Notify() uses twice the max (for background) so 5 notifications use 10 gui numbers IfNotInString, NotifyList, % "|" GN ;=== check if this notification exists Break ;=== if not, make it Else ;=== if so If (++GN > GL) ;=== increment GN, check if under maxcount Return 0 ;GN := GF ;=== If so, recheck, if not, return 0 ERROR (process by your script, if succedes returns guiID) (should save somehow and show when possible) NotifyList .= "|" GN ;=== add GN to list of open guis, | delimeted GN2 := GN + GL - GF + 1 ;=== gui number for background gui Prev_DetectHiddenWindows := A_DetectHiddenWindows ;=== save current DetectHiddenWindows settings Prev_TitleMatchMode := A_TitleMatchMode ;=== save current TitleMatchMode settings DetectHiddenWindows On ;=== needed for winexist() SetTitleMatchMode 1 ;=== needed for winexist() If (WinExist("NotifyGui")) ;=== find all Notify() gui's from any scripts for placement WinGetPos, OtherX, OtherY ;=== change this to a loop for all windows, not just first found by winexist() DetectHiddenWindows %Prev_DetectHiddenWindows% ;=== restore script settings SetTitleMatchMode %Prev_TitleMatchMode% ;=== restore script settings Gui, %GN2%:Destroy ; - remove these once unnecessary Gui, %GN%:Destroy ; - remove these once unnecessary Gui, %GN%:-Caption +ToolWindow +AlwaysOnTop -Border ;=== notification gui creation Gui, %GN%:Color, %GC% Gui, %GN%:Font, w%TW% s%TS% c%TC% If ((Image) && FileExist(Image)) { Gui, %GN%:Add, Picture, w%IW% h%IH%, % Image ImageShift = x+10 } If (Title) Gui, %GN%:Add, Text, % ImageShift, % Title Gui, %GN%:Font, w%MW% s%MS% c%MC% If ((Title) && (Message)) Gui, %GN%:Margin, , -5 If (Message) Gui, %GN%:Add, Text,, % Message If ((Title) && (Message)) Gui, %GN%:Margin, , 8 Gui, %GN%:Show, Hide, NotifyGui Gui %GN%:+LastFound WinGetPos, GX, GY, GW, GH ;=== get notification size/position Gui, %GN%:Add, Text, x0 y0 w%GW% h%GH% gNotify_Action BackgroundTrans ;=== for clicking anywhere on the notification instead of just the text If (GR) WinSet, Region, % "0-0 w" GW " h" GH " R" GR "-" GR ;=== round corners of notification, notification gui creation END WinSet, Transparent, % GT SysGet, Workspace, MonitorWorkArea ;=== initial placement NewX := WorkSpaceRight-GW-5 If (OtherY) NewY := OtherY-GH-5 Else NewY := WorkspaceBottom-GH-5 If NewY < % WorkspaceTop NewY := WorkspaceBottom-GH-5 ;=== initial placement END Gui, %GN2%:-Caption +ToolWindow +AlwaysOnTop -Border +E0x20 ;=== background gui Gui, %GN2%:Color, %BC% Gui %GN2%:+LastFound If (BR) WinSet, Region, % "0-0 w" GW+(BW*2) " h" GH+(BW*2) " R" BR "-" BR WinSet, Transparent, % BT Gui, %GN2%:Show, % "Hide x" NewX-BW " y" NewY-BW " w" GW+(BW*2) " h" GH+(BW*2) Gui, %GN%:Show, % "Hide x" NewX " y" NewY, NotifyGui Gui %GN%:+LastFound NotifyGuiID := WinExist() DllCall("AnimateWindow","UInt",NotifyGuiID,"Int",250,"UInt","0x00040008") ;=== slide up new notifications Gui, %GN2%:Show, NA ;=== show background gui WinSet, AlwaysOnTop, On If Duration < 0 Exit := GN If (Duration) ;=== set timeout for notification SetTimer, NotifyKill%GN%, % - Abs(Duration) * 1000 Else SetTimer, NotifyFlash%GN%, 150 ;=== flash border/icon for permanent notification (add option for flash/noflash) Return NotifyGuiID ;=== change to return gui number ;==================================================================================== ;==================================================================================== ;=== when a notification is clicked: Notify_Action: SetTimer, NotifyKill%A_Gui%, Off Gui, % A_Gui + 20 ":Destroy" Gui %A_Gui%:+LastFound NotifyGuiID := WinExist() DllCall("AnimateWindow","UInt",NotifyGuiID,"Int",100,"UInt", "0x00050001") ;=== slide right FAST clicked notifications Gui, %A_Gui%:Destroy If IsLabel(Notify_Action) Gosub, %Notify_Action% StringReplace, NotifyList, NotifyList, % "|" GN, , All SetTimer, % "NotifyFlash" A_Gui, Off If (Exit = A_Gui) ExitApp Return ;=== when a notification times out: NotifyKill: NotifyKill11: NotifyKill12: NotifyKill13: NotifyKill14: NotifyKill15: NotifyKill16: NotifyKill17: NotifyKill18: NotifyKill19: ;=== there's gotta be a better way to do this, bueller?, bueller? NotifyKill20: NotifyKill21: NotifyKill22: NotifyKill23: NotifyKill24: NotifyKill25: NotifyKill26: NotifyKill27: NotifyKill28: NotifyKill29: NotifyKill30: StringReplace, GK, A_ThisLabel, NotifyKill SetTimer, NotifyFlash%GK%, Off Gui, % GK + 20 ":Destroy" Gui %GK%:+LastFound NotifyGuiID := WinExist() DllCall("AnimateWindow","UInt",NotifyGuiID,"Int",300,"UInt", "0x00050001") ;=== slide right SLOW timed-out notifications Gui, %GK%:Destroy StringReplace, NotifyList, NotifyList, % "|" GK If (Exit = GK) ExitApp Return ;=== flashes a permanent notification [buggy] NotifyFlash11: NotifyFlash12: NotifyFlash13: NotifyFlash14: NotifyFlash15: NotifyFlash16: NotifyFlash17: NotifyFlash18: NotifyFlash19: NotifyFlash20: ;=== maybe instead use one timer and a FlashList like notifylist NotifyFlash21: ;=== maybe they should just all flash NotifyFlash22: NotifyFlash23: NotifyFlash24: NotifyFlash25: NotifyFlash26: NotifyFlash27: NotifyFlash28: NotifyFlash29: NotifyFlash30: NotifyFlash: StringReplace, FlashGN, A_ThisLabel, NotifyFlash FlashGN2 := FlashGN + 20 If Flashed := !Flashed Gui, %FlashGN2%:Color, Silver Else Gui, %FlashGN2%:Color, Black Return } ;=== end of Notify() === ;=== use in OnExit (or elsewhere) to wait for notifications to close before doing something [buggy] ;=== fix: Reference GN instead of GuiID, use negative forcetime to kill all open notifications (not yet implemented) Notify_Wait(ForceTime="", GN=11, MaxNotifications=20) { Loop % MaxNotifications { Gui %GN%:+LastFound If NotifyGuiID := WinExist() { WinWaitClose, , , % ForceTime If ErrorLevel { Gui, % GN + 20 ":Destroy" DllCall("AnimateWindow","UInt",NotifyGuiID,"Int",200,"UInt", "0x00050001") ;=== slide right FAST clicked notifications Gui, %GN%:Destroy } Break } GN++ } Return 1 }