NotifyIcon class supported BalloonTip.

This commit is contained in:
haru-s 2025-03-16 21:20:53 +09:00
parent e26ea5fe78
commit 2cbd47fa98
3 changed files with 225 additions and 77 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -30,6 +30,23 @@ class MainForm : Form
_notifyIcon.contextMenu.menuItems.add(menuItem1); _notifyIcon.contextMenu.menuItems.add(menuItem1);
_notifyIcon.contextMenu.menuItems.add(menuItem2); _notifyIcon.contextMenu.menuItems.add(menuItem2);
_notifyIcon.show(); _notifyIcon.show();
_notifyIcon.balloonTipTitle = "Balloon tip example";
_notifyIcon.balloonTipText = "Welcome to the D world!";
static if (false)
{
_notifyIcon.balloonTipIconStyle = BalloonTipIconStyle.INFO;
// _notifyIcon.balloonTipIconStyle = BalloonTipIconStyle.ERROR;
// _notifyIcon.balloonTipIconStyle = BalloonTipIconStyle.WARNING;
// _notifyIcon.balloonTipIconStyle = BalloonTipIconStyle.NONE;
}
else
{
_notifyIcon.balloonTipIconStyle = BalloonTipIconStyle.USER;
_notifyIcon.balloonTipIcon = new Icon(r".\image\icon2.ico");
}
_notifyIcon.balloonTipSound = true;
_notifyIcon.showBalloonTip();
} }
} }

View file

@ -5,7 +5,13 @@
/// ///
module dfl.notifyicon; module dfl.notifyicon;
private import dfl.internal.winapi, dfl.base, dfl.drawing; private import core.sys.windows.winbase;
private import core.sys.windows.windef;
private import core.sys.windows.winuser;
private import core.sys.windows.basetyps : GUID;
private import core.sys.windows.shellapi;
private import dfl.base, dfl.drawing;
private import dfl.control, dfl.form, dfl.application; private import dfl.control, dfl.form, dfl.application;
private import dfl.event, dfl.internal.utf, dfl.internal.dlib; private import dfl.event, dfl.internal.utf, dfl.internal.dlib;
@ -18,6 +24,82 @@ else
} }
// NOTE: Workaround for shellapi.h
enum NOTIFYICON_VERSION_4 = 4;
enum NIF_SHOWTIP = 0x00000080;
enum NIIF_USER = 0x00000004;
struct DFL_NOTIFYICONDATAA {
DWORD cbSize = DFL_NOTIFYICONDATAA.sizeof;
HWND hWnd;
UINT uID;
UINT uFlags;
UINT uCallbackMessage;
HICON hIcon;
CHAR[128] szTip = 0;
DWORD dwState;
DWORD dwStateMask;
CHAR[256] szInfo = 0;
union {
UINT uTimeout;
UINT uVersion;
}
CHAR[64] szInfoTitle = 0;
DWORD dwInfoFlags;
GUID guidItem;
HICON hBalloonIcon;
}
alias DFL_PNOTIFYICONDATAA = DFL_NOTIFYICONDATAA*;
struct DFL_NOTIFYICONDATAW {
DWORD cbSize = DFL_NOTIFYICONDATAW.sizeof;
HWND hWnd;
UINT uID;
UINT uFlags;
UINT uCallbackMessage;
HICON hIcon;
WCHAR[128] szTip = 0;
DWORD dwState;
DWORD dwStateMask;
WCHAR[256] szInfo = 0;
union {
UINT uTimeout;
UINT uVersion;
}
WCHAR[64] szInfoTitle = 0;
DWORD dwInfoFlags;
GUID guidItem;
HICON hBalloonIcon;
}
alias DFL_PNOTIFYICONDATAW = DFL_NOTIFYICONDATAW*;
static if (useUnicode)
{
BOOL DFL_Shell_NotifyIcon(DWORD dw, DFL_PNOTIFYICONDATAW notif)
{
return Shell_NotifyIcon(dw, cast(PNOTIFYICONDATAW)notif);
}
}
else
{
BOOL DFL_Shell_NotifyIcon(DWORD dw, DFL_PNOTIFYICONDATAA notif)
{
return Shell_NotifyIcon(dw, cast(PNOTIFYICONDATAA)notif);
}
}
///
enum BalloonTipIconStyle
{
NONE,
INFO,
WARNING,
ERROR,
USER
}
/// ///
class NotifyIcon // docmain class NotifyIcon // docmain
{ {
@ -41,15 +123,15 @@ class NotifyIcon // docmain
/// ///
final @property void icon(Icon ico) // setter final @property void icon(Icon icon) // setter
{ {
_icon = ico; _icon = icon;
nid.hIcon = ico ? ico.handle : null; _nid.hIcon = icon ? icon.handle : null;
if(visible) if(visible)
{ {
nid.uFlags = NIF_ICON; _nid.uFlags |= NIF_ICON;
Shell_NotifyIconA(NIM_MODIFY, &nid); DFL_Shell_NotifyIcon(NIM_MODIFY, &_nid);
} }
} }
@ -61,34 +143,33 @@ class NotifyIcon // docmain
/// ///
// Must be less than 64 chars.
// To-do: hold reference to setter's string, use that for getter.. ?
final @property void text(Dstring txt) // setter final @property void text(Dstring txt) // setter
{ {
if(txt.length >= nid.szTip.length) if(txt.length >= _nid.szTip.length)
throw new DflException("Notify icon text too long"); throw new DflException("Notify icon text too long");
// To-do: support Unicode. static if (useUnicode)
Dwstring str = toUnicode(txt);
txt = unsafeAnsi(txt); // ... else
nid.szTip[txt.length] = 0; Dstring str = unsafeAnsi(txt);
nid.szTip[0 .. txt.length] = txt[]; _nid.szTip[str.length] = 0;
tipLen = txt.length.toI32; _nid.szTip[0 .. str.length] = str[];
_tipLen = str.length.toI32;
if(visible) if(visible)
{ {
nid.uFlags = NIF_TIP; _nid.uFlags |= NIF_TIP;
Shell_NotifyIconA(NIM_MODIFY, &nid); DFL_Shell_NotifyIcon(NIM_MODIFY, &_nid);
} }
} }
/// ditto /// ditto
final @property Dstring text() // getter final @property Dstring text() // getter
{ {
//return nid.szTip[0 .. tipLen]; // Returning possibly mutated text! static if (useUnicode)
//return nid.szTip[0 .. tipLen].dup; return fromUnicodez(_nid.szTip[0 .. _tipLen].ptr);
//return nid.szTip[0 .. tipLen].idup; // Needed in D2. Doesn't work in D1. else
return cast(Dstring)nid.szTip[0 .. tipLen].dup; // Needed in D2. Doesn't work in D1. return cast(Dstring)_nid.szTip[0 .. _tipLen].dup;
} }
@ -97,29 +178,28 @@ class NotifyIcon // docmain
{ {
if(byes) if(byes)
{ {
if(!nid.uID) if(!_nid.uID)
{ {
nid.uID = allocNotifyIconID(); _nid.uID = allocNotifyIconID();
assert(nid.uID); assert(_nid.uID);
allNotifyIcons[nid.uID] = this; allNotifyIcons[_nid.uID] = this;
} }
_forceAdd(); _forceAdd();
} }
else if(nid.uID) else if(_nid.uID)
{ {
_forceDelete(); _forceDelete();
//delete allNotifyIcons[nid.uID]; allNotifyIcons.remove(_nid.uID);
allNotifyIcons.remove(nid.uID); _nid.uID = 0;
nid.uID = 0;
} }
} }
/// ditto /// ditto
final @property bool visible() // getter final @property bool visible() // getter
{ {
return nid.uID != 0; return _nid.uID != 0;
} }
@ -134,8 +214,74 @@ class NotifyIcon // docmain
{ {
visible = false; visible = false;
} }
///
final void showBalloonTip()
{
_nid.uFlags |= NIF_INFO;
DFL_Shell_NotifyIcon(NIM_MODIFY, &_nid);
}
///
final @property void balloonTipTitle(Dstring title) // setter
{
Dwstring str = toUnicode(title ~ '\0');
_nid.szInfoTitle[0 .. str.length] = str[];
}
///
final @property void balloonTipText(Dstring text) // setter
{
Dwstring str = toUnicode(text ~ '\0');
_nid.szInfo[0 .. str.length] = str[];
}
///
final @property void balloonTipIconStyle(BalloonTipIconStyle style) // setter
{
_nid.dwInfoFlags &= ~NIIF_ICON_MASK;
final switch (style)
{
case BalloonTipIconStyle.NONE:
_nid.dwInfoFlags |= NIIF_NONE;
break;
case BalloonTipIconStyle.INFO:
_nid.dwInfoFlags |= NIIF_INFO;
break;
case BalloonTipIconStyle.WARNING:
_nid.dwInfoFlags |= NIIF_WARNING;
break;
case BalloonTipIconStyle.ERROR:
_nid.dwInfoFlags |= NIIF_ERROR;
break;
case BalloonTipIconStyle.USER:
_nid.dwInfoFlags |= NIIF_USER;
}
}
///
final @property void balloonTipIcon(Icon icon) // setter
{
_balloonTipIcon = icon;
_nid.hBalloonIcon = icon ? icon.handle : null;
}
///
final @property void balloonTipSound(bool byes) // setter
{
if (byes)
_nid.dwInfoFlags &= ~NIIF_NOSOUND;
else
_nid.dwInfoFlags |= NIIF_NOSOUND;
}
Event!(NotifyIcon, EventArgs) click; /// Event!(NotifyIcon, EventArgs) click; ///
Event!(NotifyIcon, EventArgs) doubleClick; /// Event!(NotifyIcon, EventArgs) doubleClick; ///
Event!(NotifyIcon, MouseEventArgs) mouseDown; /// Event!(NotifyIcon, MouseEventArgs) mouseDown; ///
@ -148,34 +294,22 @@ class NotifyIcon // docmain
if(!ctrlNotifyIcon) if(!ctrlNotifyIcon)
_init(); _init();
nid.cbSize = nid.sizeof; _nid.cbSize = _nid.sizeof;
nid.hWnd = ctrlNotifyIcon.handle; _nid.hWnd = ctrlNotifyIcon.handle;
nid.uID = 0; _nid.uID = 0;
nid.uCallbackMessage = WM_NOTIFYICON; _nid.uCallbackMessage = WM_NOTIFYICON;
nid.hIcon = null; _nid.hIcon = null;
nid.szTip[0] = '\0'; _nid.szTip[0] = '\0';
} }
~this() ~this()
{ {
if(nid.uID) if(_nid.uID)
{ {
_forceDelete(); _forceDelete();
//delete allNotifyIcons[nid.uID]; allNotifyIcons.remove(_nid.uID);
allNotifyIcons.remove(nid.uID);
} }
//delete allNotifyIcons[nid.uID];
//allNotifyIcons.remove(nid.uID);
/+
if(!allNotifyIcons.length)
{
delete ctrlNotifyIcon;
ctrlNotifyIcon = null;
}
+/
} }
@ -183,11 +317,8 @@ class NotifyIcon // docmain
// Extra. // Extra.
void minimize(IWindow win) void minimize(IWindow win)
{ {
LONG style; HWND hwnd = win.handle;
HWND hwnd; LONG style = GetWindowLongPtrA(hwnd, GWL_STYLE).toI32;
hwnd = win.handle;
style = GetWindowLongPtrA(hwnd, GWL_STYLE).toI32;
if(style & WS_VISIBLE) if(style & WS_VISIBLE)
{ {
@ -211,11 +342,8 @@ class NotifyIcon // docmain
// Extra. // Extra.
void restore(IWindow win) void restore(IWindow win)
{ {
LONG style; HWND hwnd = win.handle;
HWND hwnd; LONG style = GetWindowLongPtrA(hwnd, GWL_STYLE).toI32;
hwnd = win.handle;
style = GetWindowLongPtrA(hwnd, GWL_STYLE).toI32;
if(!(style & WS_VISIBLE)) if(!(style & WS_VISIBLE))
{ {
@ -251,8 +379,11 @@ class NotifyIcon // docmain
private: private:
NOTIFYICONDATA nid; static if (useUnicode)
int tipLen = 0; DFL_NOTIFYICONDATAW _nid;
else
DFL_NOTIFYICONDATAA _nid;
int _tipLen = 0;
version(DFL_NO_MENUS) version(DFL_NO_MENUS)
{ {
} }
@ -260,19 +391,24 @@ class NotifyIcon // docmain
{ {
ContextMenu cmenu; ContextMenu cmenu;
} }
Icon _icon; Icon _icon; /// Task tray icon
Icon _balloonTipIcon; /// Balloon tip icon
package final void _forceAdd() package final void _forceAdd()
{ {
nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; _nid.uFlags |= NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_SHOWTIP;
Shell_NotifyIconA(NIM_ADD, &nid); if (_nid.hIcon)
_nid.uFlags |= NIF_ICON;
else
_nid.uFlags &= ~NIF_ICON;
DFL_Shell_NotifyIcon(NIM_ADD, &_nid);
} }
package final void _forceDelete() package final void _forceDelete()
{ {
Shell_NotifyIconA(NIM_DELETE, &nid); DFL_Shell_NotifyIcon(NIM_DELETE, &_nid);
} }
@ -291,12 +427,11 @@ class NotifyIcon // docmain
// Gets the tray area. // Gets the tray area.
static void _area(out RECT rect) static void _area(out RECT rect)
{ {
HWND hwTaskbar, hw; HWND hwTaskbar = FindWindowExA(null, null, "Shell_TrayWnd", null);
hwTaskbar = FindWindowExA(null, null, "Shell_TrayWnd", null);
if(hwTaskbar) if(hwTaskbar)
{ {
hw = FindWindowExA(hwTaskbar, null, "TrayNotifyWnd", null); HWND hw = FindWindowExA(hwTaskbar, null, "TrayNotifyWnd", null);
if(hw) if(hw)
{ {
GetWindowRect(hw, &rect); GetWindowRect(hw, &rect);
@ -373,8 +508,7 @@ class NotifyIconControl: Control
} }
Application.creatingControl(this); Application.creatingControl(this);
hwnd = CreateWindowExA(wexstyle, CONTROL_CLASSNAME.ptr, "NotifyIcon", 0, 0, 0, 0, 0, null, null, hwnd = CreateWindowExA(wexstyle, CONTROL_CLASSNAME.ptr, "NotifyIcon", 0, 0, 0, 0, 0, null, null, Application.getInstance(), null);
Application.getInstance(), null);
if(!hwnd) if(!hwnd)
goto create_err; goto create_err;
} }
@ -386,11 +520,9 @@ class NotifyIconControl: Control
{ {
if(cast(UINT)msg.wParam in allNotifyIcons) if(cast(UINT)msg.wParam in allNotifyIcons)
{ {
NotifyIcon ni; NotifyIcon ni = allNotifyIcons[cast(UINT)msg.wParam];
Point pt; Point pt;
ni = allNotifyIcons[cast(UINT)msg.wParam];
switch(cast(UINT)msg.lParam) // msg. switch(cast(UINT)msg.lParam) // msg.
{ {
case WM_MOUSEMOVE: case WM_MOUSEMOVE:
@ -468,8 +600,7 @@ static ~this()
UINT allocNotifyIconID() UINT allocNotifyIconID()
{ {
UINT prev; UINT prev = lastId;
prev = lastId;
for(;;) for(;;)
{ {
lastId++; lastId++;