[Win32] Bring Trayicon tooltip to front?

BlitzMax Forums/MaxGUI Module/[Win32] Bring Trayicon tooltip to front?

Grisu(Posted 2015) [#1]
Hello all,

the code below is a simple example how to set up a trayicon.

' TrayIcon 

Framework Maxgui.Drivers
Import BRL.EventQueue
Import "object.o"         ' <- YOU NEED TO CHANGE THAT TO ANOTHER ICON OBJ FILE OR COMMENT IT OUT (= NO ICON)

SuperStrict 
AppTitle = "TrayIcon Example"

?Win32
' Tray icon related stuff
Const NIM_ADD:Int		= 0
Const NIM_MODIFY:Int	= 1
Const NIM_DELETE:Int	= 2
Const NIM_SETFOCUS:Int	= 3
Const NIM_SETVERSION:Int= 4

Const NIF_MESSAGE:Int	= $1
Const NIF_ICON:Int		= $2
Const NIF_TIP:Int		= $4
Const NIF_STATE:Int		= $8
Const NIF_INFO:Int		= $10
Const NIF_GUID:Int		= $20

Global nid:TNotifyIconData

Type TNotifyIconData 
	Field Size:Int
	Field HWND:Int
	Field id:Int
	Field Flags:Int
	Field CallbackMessage:Int
	Field Icon:Int 				' HICON	
	Field Tip:Long				' array [0..63] of AnsiChar;
	Field Tip2:Long
	Field Tip3:Long
	Field Tip4:Long
	Field Tip5:Long
	Field Tip6:Long
	Field Tip7:Long
	Field Tip8:Long
EndType

Extern "win32"
	Function Shell_NotifyIcon:Int(message:Int, notifyicondata:Byte Ptr) = "Shell_NotifyIconA@8"
End Extern
?

Local FLAGS:Int
FLAGS:| WINDOW_TITLEBAR
FLAGS:| WINDOW_RESIZABLE
FLAGS:| WINDOW_MENU
FLAGS:| WINDOW_CLIENTCOORDS
'FLAGS:| WINDOW_HIDDEN
FLAGS:| WINDOW_ACCEPTFILES

Global MainWindow:TGadget = CreateWindow( AppTitle, 100, 100, 320, 240, Null, FLAGS )
Global TrayIconPanel:TGadget = CreatePanel(0, 0, 0, 0, MainWindow, 0 )

Global win_trayed:Int=0

Repeat
	WaitEvent()
	Print CurrentEvent.ToString()
	Select EventID()
		Case EVENT_AppSuspend
        '   DebugLog "App now suspended!"

		If WindowMinimized(Mainwindow) Then 
?win32
			HideGadget(MainWindow)

			RegisterTrayIcon(TrayIconPanel, "This is premade Tooltip!") 'show station currently on AIR!
?
       EndIf 

	Case EVENT_MOUSEDOWN
		Select EventSource()
		Case TrayIconPanel
			If EventData()=1 Then
				RemoveTrayIcon()
				RedrawGadget(Mainwindow) 
				ShowGadget(MainWindow)
				RedrawGadget (MainWindow)
				RestoreWindow(Mainwindow)
			EndIf 

			If EventData()=2 Then
			    ' Set a new Tooltip when you hit the right mouse button over the trayicon
		   		SetNotifyIconDataTip( nid, "I'm a reborn Tooltip! :)" )
				Shell_NotifyIcon( NIM_MODIFY, nid)
	 		EndIf

		End Select
   
		Case EVENT_APPTERMINATE, EVENT_WINDOWCLOSE
			End
	End Select
Forever

' Tray Icon Functions, Win 32 only! ----------------------------------------------------------
Function RegisterTrayIcon( window:TGadget, toolTip:String )
' Register tray icon

	?Win32
	nid = New TNotifyIconData
	nid.Size = SizeOf(TNotifyIconData)
	nid.hwnd = QueryGadget(window, QUERY_HWND)
	SetWindowLongW(nid.hwnd,GWL_WNDPROC,Int Byte Ptr SysTrayWndProc)
	nid.id = 0
	nid.CallbackMessage = WM_APP
	nid.Icon = LoadIconW(GetModuleHandleW(Null),Short Ptr(101))
	nid.Flags = NIF_MESSAGE | NIF_TIP | NIF_ICON  
	SetNotifyIconDataTip( nid, toolTip )

	Shell_NotifyIcon( NIM_ADD, nid)
	?
	
	Win_trayed:Int=1
End Function

Function SysTrayWndProc%(hwnd%,msg%,wp%,lp%) "win32"
'WndProc NotifyIcon -> Blitz Event
	?Win32
	Select msg 
		Case WM_APP
                 Local owner:TGadget = TWindowsGUIDriver.GadgetFromHwnd(hwnd)
			If owner Then
				Select lp
				     Case WM_MOUSELEAVE
						PostGuiEvent( EVENT_MOUSELEAVE, owner )
 				     Case WM_MOUSEMOVE
						PostGuiEvent( EVENT_MOUSEMOVE, owner )
					Case WM_RBUTTONDOWN
						PostGuiEvent( EVENT_MOUSEDOWN, owner, MOUSE_RIGHT )
					Case WM_LBUTTONDOWN
						PostGuiEvent( EVENT_MOUSEDOWN, owner, MOUSE_LEFT )
					Case WM_RBUTTONDOWN	
						PostGuiEvent( EVENT_MOUSEDOWN, owner, MOUSE_RIGHT )
				EndSelect
			EndIf
	EndSelect
	Return TWindowsGUIDriver.ClassWndProc(hwnd,msg,wp,lp)
	?
EndFunction

Function RemoveTrayIcon()    
' Remove tray icon from taskbar
	?Win32
	Shell_NotifyIcon(NIM_DELETE, nid)
    win_trayed=0
	?
End Function

?Win32     
Function SetNotifyIconDataTip( nid:TNotifyIconData, s:String)
	MemClear( Varptr nid.Tip, 64)
	If s.length > 0 Then
		Local p:Byte Ptr = s.ToCString()
		If s.length < 64 Then
			MemCopy( Varptr nid.Tip, p, s.length)
		Else			
			MemCopy( Varptr nid.Tip, p, 63)			
		EndIf
		MemFree(p)
	EndIf
EndFunction
?	


I'm trying to figure out how I can change the tooltip of a trayicon so that it shows up in front. This way the user doesn't need to move the mouse over it in order to make it visible.

Is that possible?

Some research I did:
https://msdn.microsoft.com/en-us/library/windows/desktop/bb773352%28v=vs.85%29.aspx
https://msdn.microsoft.com/de-de/library/windows/desktop/hh298401%28v=vs.85%29.aspx

But I couldn't find a clue how to do that. :/


Henri(Posted 2015) [#2]
Hi,

there is the balloon infotip available which can be popuped with 'ShellNotifyInfo' function. I can take a look at it later if you want.

-Henri


col(Posted 2015) [#3]
A 'tooltip' by definition will show only when the user moves the mouse over an icon that has a tooltip associated with it. So what you have would be 'expected behavaiour'.
An 'infotip' on the other hand can be made to show information using the Shell_NotifyInfo function using a popup ( which you get when you plug in or remove a usb device into a usb port ). If you fill out the rest of the NOTIFYICONINFO structure ( you have the info and guid etc parts missing ) then it should work.


Grisu(Posted 2015) [#4]
@Henri:
If you could take a look, that would be nice. I don't know anything about this API stuff.

As far as I know, balloon tips aren't supported by lower Windows versions. So I'd like to make this optional. Just in case the OS doesn't support it or the user doesn't want it (i.e. "If balloon_tip=true and OS > WinXP then").

I would use the code to make a small notification when a new song is played and my apps sits in the system tray.


Henri(Posted 2015) [#5]
I'll try to work it out on weekend.

-Henri


Grisu(Posted 2015) [#6]
Great, thanks a lot.


col(Posted 2015) [#7]
Hiya,

As far as I know, balloon tips aren't supported by lower Windows versions.

Infotips ( or balloon tips ) are supported down to and including XP, but that's it.


Henri(Posted 2015) [#8]
Allrighty,

here is a working example for displaying balloon info in taskbar. I haven't tested it in other than Windows 7 64-bit, but should work from XP to Win 8 (if not then do let me know). This version is ANSI-standard so not sure how it behaves in all localizations, but making UNICODE version isn't that difficult if needed. I would perhaps wrap all this functionality inside a type just for neatness, but thats optional.

EDIT: Updated code for Unicode support and few fixes.

EDIT2: Fixed EditInfo() so that if no parameters are supplied
then the balloontip is disabled.

Example:


-Henri


Grisu(Posted 2015) [#9]
Thanks for digging into this.

I'm need of a unicode version if that is possible. Because some stations transmit data in Cyrillic for instance.

So far it seems to work:


Only thing I have noticed under windows 8 is that when I edited a balloon-tip, the normal tooltip for the tray icon itself will no longer show up if you move the mouse over it.

Use the line "RegisterTrayIcon(TrayIconPanel, "THIS IS MY COOL APP")" as replacement for your example code above.

Still testing...


Henri(Posted 2015) [#10]
No problem, I'll see about that unicode version and tooltip.

-Henri


Grisu(Posted 2015) [#11]
Thanks Henri! Do you have an e-mail address where I can contact you?


Henri(Posted 2015) [#12]
OK, I've updated the example above for Unicode support and couple of fixes.


You can contact me at kp9176<at>gmail.com .I usually don't watch it frequently, but if you post within a week I can get back to you from a proper email address.

-Henri


Grisu(Posted 2015) [#13]
Hi again!

For my testing the new code works fine, I only ran into one issue.

I need to enable and disable the tips from within the app.

For this I use a simple option "ini_dis_balloontip" to determine this.

It works if I have the balloontips disabled and then enable them.
But if they are disabled after being enabled, they always appear. Even though the EditInfo() and ShowInfo() aren't called anymore?

Example function code:
Function RegisterTrayIcon( window:TGadget, toolTip:String="" )
' Register tray icon

	?Win32
	Titlebak:String=""
	Artistbak:String=""
		If Not nid Then

		nid = New TNotifyIconData
		nid.Size = SizeOf(TNotifyIconData)
		nid.hwnd = QueryGadget(window, QUERY_HWND)
		
		SetWindowLongW(nid.hwnd,GWL_WNDPROC,Int Byte Ptr SysTrayWndProc)
		
		nid.CallbackMessage = WM_APP
		nid.Icon = LoadIconW(GetModuleHandleW(Null),Short Ptr(101))

		nid.Flags = NIF_MESSAGE | NIF_TIP | NIF_ICON | NIF_INFO
	EndIf
	
	If toolTip Then _stringCopy( Varptr nid.Tip, toolTip, 128)

        'Print "ini dis ball: "+ini_dis_balloontip
	If ini_dis_balloontip=0 Then 
	 EditInfo(station_genre, station_name)
    	 Showinfo()  
       EndIf

	Shell_NotifyIcon(NIM_ADD, nid)

	?
	
	prp_trayed:Int=1
End Function


Any idea, what might cause this behaviour?

P.S. You've got mail.


Henri(Posted 2015) [#14]
I have revised the code above (see EDIT2:).


P.S. You've got mail.
Answered.

-Henri


Chalky(Posted 2015) [#15]
.


Grisu(Posted 2015) [#16]
I made a function to only update the tooltip itself.

Function EditTrayTooltip(toolTip:String="")
?win32
 	If toolTip Then _stringCopy( Varptr nid.Tip, toolTip, 128)
	Shell_NotifyIcon( NIM_ADD, nid)
?
End Function 


The only issue left is that when the balloon tips are disabled, the function does no longer update the tooltip?


Henri(Posted 2015) [#17]
Hi,

only thing I see a bit off is the use of NIM_ADD instead of NIM_MODIFY ?

-Henri


Grisu(Posted 2015) [#18]
When I use NIM_MODIFY, the balloon tip will show up. But the tooltip on the tray icon stays the same.

I only need to set the tooltip on an already registered tray icon. I could remove and set it again for each new song, but this would look strange. :)

I uploaded the latest prp testbuild (III) to my website. If you want to see, how it currently works.


Henri(Posted 2015) [#19]
I see :-).

Ok, modify ShowInfo() function a bit like so:
Function ShowInfo:Int()
	If nid Then
		nid.flags = NIF_MESSAGE | NIF_TIP | NIF_ICON | NIF_INFO
		Shell_NotifyIcon( NIM_MODIFY, nid)
	EndIf
EndFunction

Now modify the EditTrayTooltip() function a bit like so:
Function EditTrayTooltip(toolTip:String="")
?win32
 	If toolTip Then _stringCopy( Varptr nid.Tip, toolTip, 128)
	nid.flags = NIF_MESSAGE | NIF_TIP | NIF_ICON
	Shell_NotifyIcon( NIM_MODIFY, nid)
?
End Function 


And here is the newest version of the example in whole:



-Henri


Grisu(Posted 2015) [#20]
Not working as expected. The balloon code isn't reliable from my testing so far.

Guess, I will fall back to the old code. More testing over the weekend.


Henri(Posted 2015) [#21]
Okey,

I made some 'slight' modifications. Sorry if it's a 'bit' different , but I think it will be cleaner and more OO.



-Henri


Grisu(Posted 2015) [#22]
Hi, under Win8 your newest code instantly closes the balloon tip the second time you resink the app into the task bar on Win 8.1.

1. run exampled code
2. minimise app -> balloon shows up for 3 seconds.
3. maximise app
4. minimise app again -> no ballon shows or only for a blink of time.

Will try to implement your new code structure into my main code now.


Henri(Posted 2015) [#23]
Hmm, it's probably not a Win8 issue as it seems to work fine on my Win8. I believe whenever app is added to systemtray it's automatically focused and if you press somewhere else causing it to lose focus ignites the fading out pocess for the balloon tip. Maybe one thing to try out as Win8 notification time is defaulted to 5 seconds so you could try to change this from the 'Easy access settings' to longer time to see if it makes any difference.

EDIT: Could you send me your manifest file for testing ?

-Henri


Grisu(Posted 2015) [#24]
Hello again. Thanks for all your help.

After spending my saturday on this matter. I'm going back to the old code as I couldn't get it to work properly.