Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Notify() - multiple easy tray area notifications - v0.4991


  • Please log in to reply
319 replies to this topic
gwarble
  • Members
  • 624 posts
  • Last active: Aug 12 2016 07:49 PM
  • Joined: 23 May 2009

i wrote this function to create and position multiple simultaneous tray area notifications, from any number of different scripts... Notify() can display fully-customizable, timed (or not) notification windows that can display any type of information quickly and easily (i like using it for debugging; Notify("var",var) works well), each of which can perform actions when clicked or time out, etc... try it out and let me know what you think!

stdlib compliant, just drop in your Lib folder and call Notify() to see it in action (no globals, no dependencies)

Notify() v0.4991 - Download Notify.ahk or read the incomplete Documentation

Screenshots:
test5.gif
 

Examples:

Notify() ;=== creates a default notification
Title= Hello
Message= World
Duration=0
Options:="GC=bbffbb TC=White MC=White Image=14"
Notify(Title,Message,Duration,Options) ;= pretty simple
Notify("Hello","World",0,"GC=bbffbb TC=White MC=White Image=" 14) ;= same
Notify("Title","Message",10) ;=== typical call
Loop 10 ;=== creates 10 notifications with varying options
Notify("Notify " A_Index, "Duration=" Abs(20-A_Index-Mod(A_Index,3)*A_Index) " Corners=" Round((A_Index*A_Index-1)/3) " Icon=" A_Index+35,Abs(20-A_Index-Mod(A_Index,3)*A_Index),"GR=" (A_Index*A_Index-1)/3 " BR=" (A_Index*A_Index-1)/3 " IN=" A_Index+35)

Action/Wait Demo:

Notify("Welcome to Notify()","Click this notification to open a new one using AC=...",0,"AC=ActionDemo IN=8 IW=48 IH=48", A_WinDir "\explorer.exe")
Return
ActionDemo:
HotKey, ^F12, WaitDemo
NotifyID := Notify("","this one is permanent, press ctrl-F12 to force kill after 3 seconds",0)
Return
WaitDemo:
Notify("","",-3,"Wait=" NotifyID)
Notify("Waiting done!","It was killed after 3 seconds, even though it had 0 duration!`n`nthis notification will exitapp if clicked or waits 15 seconds",-15)
Return

Notify() 0.4991
by gwarble
september 2009 - current

 

thanks to Rhys/engunneer [Toaster Popups] and HugoV [help/recommendations]

i'm new to releasing my code and would greatly appreciate any feedback/suggestions/ideas/criticism/etc... on this function, which is similar to/can replace: TP Toaster Popup, TrayTip and Menu Tray Tip, some ToolTips, and has similar functionality to snarl and growl for tray notifications



SoLong&Thx4AllTheFish
  • Members
  • 4999 posts
  • Last active:
  • Joined: 27 May 2007
Nice and easy to use. In NotifyWait there is a reference to an icon in notifier.exe you may wish to comment out for people just downloading notify.ahk

gwarble
  • Members
  • 624 posts
  • Last active: Aug 12 2016 07:49 PM
  • Joined: 23 May 2009
thanks, fixed

also made the function file alone able to run for testing, so you can just download Notify.ahk and run it without Notifier or including:

^+F12 for test notifications, doesnt show all options but lets you see the multiple window detection (also, run 2 copies of it to see how it handles two scripts using notify() )

Edit: code updated in initial post

thanks for trying it out

- gwarble

=====================================
anyone know a better way to have seperate timers/timer lengths (dynamically) than this cob solution:
 SetTimer, NotifyKill%GuiNumber%, % "-" Duration * 1000 ;in function call
 ...
NotifyKill11: ;function subs
NotifyKill12:
NotifyKill13:
NotifyKill14:
NotifyKill15:
NotifyKill16:
NotifyKill17:
NotifyKill18:
NotifyKill19: 
NotifyKill20: 
NotifyKill:
 StringReplace, GuiNumber, A_ThisLabel, NotifyKill ;crappy dynamic timer solution
 GuiNumber2 := GuiNumber + 20
 Gui, %GuiNumber2%:Destroy
 Gui  %GuiNumber%:+LastFound
 NotifyGuiID := WinExist()
 DllCall("AnimateWindow","UInt",NotifyGuiID,"Int",600,"UInt", "0x00050001") 	;=== slide right SLOW timed-out notifications
 Gui, %GuiNumber%:Destroy
 NotifyCount--
 SetTimer, NotifyFlash, Off
 If (Exit)
  ExitApp
Return
there's gotta be a better way

gwarble
  • Members
  • 624 posts
  • Last active: Aug 12 2016 07:49 PM
  • Joined: 23 May 2009
worked out all the kinks for multiple guis, now need to clean up the code a bit, but now multiple can show, while each retaining its own timer

here's some screenshots of it in use in my other app:

i'd love to see it in use in your scripts
- gwarble

Posted ImagePosted Image

SoLong&Thx4AllTheFish
  • Members
  • 4999 posts
  • Last active:
  • Joined: 27 May 2007
Some ideas:

- I prefer rectangular notifications vs rounded, perhaps you could introduce a style or rounding parameter for the winset

- I also use ToasterPopups which I modified to be able to include images in the TP

- The border is drawn with transparency perhaps allow colours so you can have a red border for example

Edit: re timer question, for some ideas see <!-- m -->http://www.autohotke...pic.php?t=48670<!-- m -->, lexikos post

gwarble
  • Members
  • 624 posts
  • Last active: Aug 12 2016 07:49 PM
  • Joined: 23 May 2009
for now its easy to comment out the lines for winset,region (rounded corners) and manually change the background guis color (or if you want it to look like a tooltip you could drop the second gui altogether)

to make it parameter based, would you prefer
Notify(title,msg,dur,......,colorthis,colorthat,thicknessthis,thicknessthat)
with tons of parameters, or:
Notify(title,msg,dur,action, "FClrRed GClrBlue BClrGreen FSize12 FWght500")
letting notify parse a single parameter for all these options

i'm thinking the latter, which could have lots easily including:
font size,color,titlesize,messagesize,font face,weight,modifiers(italic etc)
gui color,size,flashcolor, backgroundcolor, opacity, clickthru or not, clickaction and timeoutaction, background border thickness, lots more, gui numbers, etc

let me know what approach to go which is most appreciated
-gwarble

EDIT:
oh yeah, and i'm adding images at the moment, the whole reason i made this function is for that last screenshot, to replace the bottom gui with this dynamically positioning Notify() gui, so images are being implemented now, but again i'd love to know the way you would prefer to call the function

SoLong&Thx4AllTheFish
  • Members
  • 4999 posts
  • Last active:
  • Joined: 27 May 2007
Vote+ for the images 8)

For the parameters, some suggestions close to yours:
<!-- m -->http://www.autohotke... ... parameters<!-- m -->
<!-- m -->http://www.autohotke... ... 766#280766<!-- m -->

Edit: alternative could be an NotifyInit() function where a used can define some prefered standards like style, font, color, border etc which would be global vars of course __NotifyStyle=Rounded, __NotifyFGC=cc0000 etc

gwarble
  • Members
  • 624 posts
  • Last active: Aug 12 2016 07:49 PM
  • Joined: 23 May 2009
i like the init function idea, but i don't like having globals

how about using either:

Notify("Notify_Configure",blah,blah.....blah,blah)

which would prevent a notification called Notify_Configure or something, and set static variables this way...

or i think the best is the options parameter, which set statics if used, but statics are default if they are never used, therefore not needing to be specified each call unless you want it to change... so:

Options := "TS10 TW600 TCBlack MS8 MW400 MCBlue GN11 GCFF44FF GT200" ;...
Notify(Title,Message,Duration,Action,Options)
;TS is Title Font Size
;TW is Title Font Weight
;TF is Title Font
;TC is Title Font Color
;
;MS is Message Font Size
;MW is Message Font Weight
;MF is Message Font
;MC is Message Font Color
;
;GF is First Gui Number available
;GL is Last Gui Number available ;difference is max #
;GC is Gui Color
;GR is Gui Rounded Corners (in pixels)
;
;BC is Border (Background Gui) Color
;BT is Border (Background Gui) Border Thickness
;BF is Border (Background gui) flash speed
;
;there's more that could be added, and they could be added later without changing any parameters/break functionality

also, would you want a title, message, and an image? or IfFileExist, title then show the image and now have a title

- gwarble

edit: if the first option is used Notify("Notify_Configure") then i could also use Notify("Notify_Wait") and loose the second function

SoLong&Thx4AllTheFish
  • Members
  • 4999 posts
  • Last active:
  • Joined: 27 May 2007
I would prefer TS=10 over TS10 as you can see from the examples above it is easier for the user to understand and parse. I don't mind much how you do it either with init or options, currently I use Notify(A_ScriptName, "Done", -3) a lot so that is nice and short, images, colours & styles are just a bonus :wink:

  • Guests
  • Last active:
  • Joined: --
oh crap are you using -3 to exitapp after the three seconds? cuz i removed that functionality, thinking you could use Notify(title,msg,3,"ExitSub") as an action...

but if yo ulike it i kind of do too, and its easy to have in there
-gwarble

gwarble
  • Members
  • 624 posts
  • Last active: Aug 12 2016 07:49 PM
  • Joined: 23 May 2009
or if it saves all variables as static,

You could call:
Notify(A_ScriptName, "Done", -3)
once
and then call
Notify()
every other time for the same notification

also, personally i don't like the "=" in the options since it isnt consistent with other uses of =, of which there are already 2... but i'm not opposed and will start that way... if its there the option names could be long and descriptive, but i kind of like the two letter approach...
??
- gwarble

edit: do we want:
Notify(Title,Msg,Image,Dur...
or image file in with the options
different uses would make either one more convenient... although my use would call for a different image being used on each notification (having a dedicated parameter being easier), i think most people would set the image to an image for the script and use it for all notifications... especially with two scripts using notify()

- gwarble

SoLong&Thx4AllTheFish
  • Members
  • 4999 posts
  • Last active:
  • Joined: 27 May 2007
Edit: you types while I was typing. So this refers to -3

I like it yes, the Notify is so much nicer to have then the regular MsgBox Done to indicate when a lenghty script is, well, done :wink:

Currently I use costum (per script) toasterpopups to either use with windows taskschedular to remind me of stuff or give feedback while a script is running, in all of these cases I like the script to close after the last notify (its done), so by using -3 it saves me a line in the script and it is easier to type then an another notify command/line.

Re: images mmm not sure what would be best, perhaps per notify so you can use different images for start/working/paused/ready/exit type of messages?

Perhaps also introduce some basic system icons as with MsgBox? That way you don't have to include an image with a script that uses Notify.

gwarble
  • Members
  • 624 posts
  • Last active: Aug 12 2016 07:49 PM
  • Joined: 23 May 2009
great feedback, thanks, back in it goes

SoLong&Thx4AllTheFish
  • Members
  • 4999 posts
  • Last active:
  • Joined: 27 May 2007
I was typing again while you was reading/typing so just to let you know: look above 8)

gwarble
  • Members
  • 624 posts
  • Last active: Aug 12 2016 07:49 PM
  • Joined: 23 May 2009
sounds good man, thanks for the feedback

i'm putting back in -duration, adding images, and converting to an options parameter now, i'll get a new version up shortly

- gwarble

edit:
HugoV:
got back to it, no images yet but try this for no corner rounding, option testing, color control, let me know what oyu think about the usage syntax for your script

;
;    Notify() Version 0.2
;  made by gwarble - sept 09
;
; for displaying multiple tray area notifications
;
;    Notify([Message,Title,Duration,Action,FontColor,FontSize,GuiColor,StartGN,MaxGuiCount])
;
; thanks to Rhys and engunneer [Toaster Popups]
; test with [Ctrl-Shift-1] [^+1] 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
; Notify("Notify()","This is a sample notification.`nIt will last for 15 seconds unless clicked.`nNotify.ahk is meant to be #Include-d.`nDownload Notifier for a working example.`n`nCtrl-Shift-F12 for more...`n`nThanks for trying!",15)
 Notify("f","f",13,"TC=Red GR=2 BT=2")
 Notify("f","f",13,"TC=Red GR=4 BT=2")
 Notify("f","f",13,"TC=Red GR=6 BT=2")
 Notify("f","f",13,"TC=Red GR=9 BT=2 BC=Red")
 Notify()
 Notify()
 Notify()
 Hotkey, ^+F12, Notify_Demo2
Return
Notify_Demo2:
 Notify()
 Notify("Notification:","This one won't go away unless clicked",0)
 Hotkey, ^+F12, Notify_Demo3
Return
Notify_Demo3:
; NotifyID := Notify("Test Notify","This notification will close in 2 seconds...", 2, "","Red", 8, "Black")
 NotifyID := Notify()
 Hotkey, ^+F12, Notify_Demo1
Return

;====================================================================================
;====================================================================================

Notify(Title="Notification!!!",Message="",Duration=15,Options="")
{
 Static							;=== assume-static mode for arrays of options
 Critical						;=== don't allow interruption
 ;Global Notify_Action := Action				;=== global Action label name, i don't know a way to keep actions but not have a global

 static GF := 11 	;GF is Initial GN		;=== defaults if no options
 static GL := 30		;GM is Last GN			;=== options are saved between calls (ie only set once)
 static GC := "FFFFAA"	;GC is Gui Color
 static GR := 9		;GR is Gui Radius
 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 Color
 static BT := 2		;BT is Border Thickness
 static BF := 150	;BF is Border Flash Speed
 static AC := ""	;AC is Clicked Action (gosub label when clicked)
 static AT := ""	;AT is Action Timeout (gosub label when timeout)
 static AI := ""	;AI is Action In (not really useful, is it? an action upon creation)
 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)

 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 = (GF=1) means value starts at 4 (needs to strip quotes if used)

 GN := GF
 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
    GN := GF ;Return 0						;=== 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 (Title)
  Gui, %GN%:Add, Text, gNotify_Action, % Title
 Gui, %GN%:Font, w%MW% s%MS% c%MC%
 If ((Title) && (Message))
  Gui, %GN%:Margin, , -5
 If (Message)
  Gui, %GN%:Add, Text, gNotify_Action, % Message
 If ((Title) && (Message))
  Gui, %GN%:Margin, , 8
 Gui, %GN%:Show, Hide, NotifyGui
 Gui  %GN%:+LastFound
 WinGetPos, GUIX, GUIY, GW, GH
 Gui, %GN%:Add, Text, x0 y0 w%GW% h%GH% gNotify_Action BackgroundTrans ;=== for clicking anywhere on the notification instead of just the text
 WinSet, Region, % "0-0 w" GW " h" GH " R" GR "-" GR	;=== round corners of notification, notification gui creation END

 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
 WinSet, Region, % "0-0 w" GW+(BT*2) " h" GH+(BT*2) " R" GR "-" GR
 WinSet, Transparent, 105						;=== background gui END

 Gui, %GN2%:Show, % "Hide x" NewX-BT " y" NewY-BT " w" GW+3 " h" GH+3
 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
  Notify_Exit%GN% = 1
 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
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, GN, A_ThisLabel, NotifyKill
 SetTimer, NotifyFlash%GN%, Off
 Gui, % GN + 20 ":Destroy"
 Gui  %GN%:+LastFound
 NotifyGuiID := WinExist()
 DllCall("AnimateWindow","UInt",NotifyGuiID,"Int",300,"UInt", "0x00050001") 	;=== slide right SLOW timed-out notifications
 Gui, %GN%:Destroy
 StringReplace, NotifyList, NotifyList, % "|" GN
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
}


edit: oops, i guess yo uneed all those statics so they dont get reinitialized

edit: ok version 0.3 is updated in first post