WTL: Подсказка в любом месте экрана
В своё время меня не устроил стандартный CTooltipCtrl -- мне была нужена подсказка которая бы появлялась и пропадала когда ей скажут. Кроме того, нужно было, что бы подсказка была прозрачна для всех сообщений мыши (я делал т.н. inplace tooltip). Этот простой класс делает всё вышеперечисленное.
Короткое описание:
m_hWndOwner -- окно, которое будет получать все сообщения мыши
DelayShowAtCursor( UINT time ) -- показать посказку под курсором через определённое время. Не сказать что бы самая востребованная функция, но нне это было нужно.
Остальное должно быть понятно
Маленькое замечание: при создании нужно указать окно хозяин, и это не тоже самое что окно владелец(m_hWndOwner). В качестве хозяина удобнее всего установить ::GetDesktopWindow(), в противном случае возникают малопонятные глюки с позиционированием.
namespace UI2
{
class Tooltip
: public CWindowImpl<Tooltip, ATL::CWindow>
{
public :
typedef CWindowImpl<Tooltip, ATL::CWindow> baseClass;
typedef Tooltip thisClass;
HWND m_hWndOwner;
private :
TOOLINFO m_ToolInfo;
bool m_bActive;
enum { TIMER_MSG_DELAY_SHOW = 100, TIMER_MSG_DELAY_HIDE = 101, };
static LPCTSTR GetWndClassName(){ return _T("Zaebis_Tooltip" ); }
public :
DECLARE_WND_SUPERCLASS(GetWndClassName(), baseClass::GetWndClassName())
BEGIN_MSG_MAP(thisClass)
MESSAGE_HANDLER(WM_TIMER, OnTimer)
MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
END_MSG_MAP()
Tooltip()
: baseClass()
, m_bActive(false ) // подсказка видна
, m_hWndOwner(0) // окно, которому будут передаваться мышиные сообщения
{
}
virtual ~Tooltip()
{ }
bool Create( HWND hWndParent, HWND hWndOwner = NULL )
{
HWND hWnd = ::CreateWindowEx(WS_EX_TOPMOST,
TOOLTIPS_CLASS,
NULL,
TTS_NOPREFIX | TTS_ALWAYSTIP,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
hWndParent,
NULL,
NULL,
NULL);
ATLASSERT(hWnd);
// initialize toolinfo struct
memset(&m_ToolInfo, 0, sizeof (m_ToolInfo));
m_ToolInfo.cbSize = sizeof (m_ToolInfo);
m_ToolInfo.uFlags = TTF_TRACK | TTF_TRANSPARENT | TTF_SUBCLASS;
m_ToolInfo.hwnd = hWndParent;
::SendMessage(hWnd, TTM_SETMAXTIPWIDTH, 0, SHRT_MAX);
::SendMessage(hWnd, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &m_ToolInfo);
m_hWndOwner = hWndOwner;
SubclassWindow( hWnd );
return true ;
}
void SetBackColor( COLORREF c ){ ::SendMessage(m_hWnd, TTM_SETTIPBKCOLOR, c, 0); }
void SetTextColor( COLORREF c ){ ::SendMessage(m_hWnd, TTM_SETTIPTEXTCOLOR, c, 0); }
void SetMargins( const RECT* rectMargins ){ ::SendMessage(m_hWnd, TTM_SETMARGIN, 0, (LPARAM)rectMargins); }
void SetMaxWidth( int maxWidth ){ ::SendMessage(m_hWnd, TTM_SETMAXTIPWIDTH, 0, (LPARAM)maxWidth); }
bool IsVisible(){ return m_bActive; };
CString GetText()
{
::SendMessage( m_hWnd, TTM_GETTEXT, 0, m_ToolInfo );
return CString( m_ToolInfo.lpszText );
}
void SetText( const TCHAR* str )
{
delete m_ToolInfo.lpszText;
if ( str )
{
m_ToolInfo.lpszText = new TCHAR[ _tcslen(str)+1 ];
_tcscpy( m_ToolInfo.lpszText, str );
}
else m_ToolInfo.lpszText = NULL;
::SendMessage( m_hWnd, TTM_UPDATETIPTEXT, 0, (LPARAM)(LPTOOLINFO) &m_ToolInfo );
}
void Show( int x, int y )
{
m_bActive = true ;
::SendMessage( m_hWnd, TTM_TRACKPOSITION, 0, (LPARAM)(DWORD) MAKELONG( x, y ) );
::SendMessage( m_hWnd, TTM_TRACKACTIVATE, TRUE, (LPARAM)(LPTOOLINFO) &m_ToolInfo );
}
void Show( const RECT* r )
{
m_bActive = true ;
m_ToolInfo.rect = *r;
::SendMessage( m_hWnd, TTM_TRACKPOSITION, 0, (LPARAM)(DWORD) MAKELONG( r->left, r->top ) );
::SendMessage( m_hWnd, TTM_SETMAXTIPWIDTH, 0, (LPARAM)(r->right - r->left) );
::SendMessage( m_hWnd, TTM_TRACKACTIVATE, TRUE, (LPARAM)(LPTOOLINFO)&m_ToolInfo );
}
void Show( BOOL bShow = TRUE )
{
KillTimer( TIMER_MSG_DELAY_SHOW );
KillTimer( TIMER_MSG_DELAY_HIDE );
m_bActive = (bShow == TRUE);
::SendMessage( m_hWnd, TTM_TRACKACTIVATE, bShow, (LPARAM)(LPTOOLINFO) &m_ToolInfo );
}
void Move( int x, int y )
{
::SendMessage( m_hWnd, TTM_TRACKPOSITION, 0, (LPARAM)(DWORD) MAKELONG( x, y ) );
}
void DelayShowAtCursor( UINT timeToShow )
{
KillTimer( TIMER_MSG_DELAY_SHOW );
SetTimer( TIMER_MSG_DELAY_SHOW, timeToShow );
}
operator HWND() { return m_hWnd; }
// Message handlers
LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if ( TIMER_MSG_DELAY_SHOW == wParam )
{
POINT pt; ::GetCursorPos( &pt );
Show( pt.x, pt.y+20 );
KillTimer( wParam );
}
else if ( TIMER_MSG_DELAY_HIDE == wParam )
{
Show(FALSE);
KillTimer( wParam );
}
bHandled = FALSE;
return 0;
}
LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// forward message to owner window
if ( m_hWndOwner )
{
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
ClientToScreen( &pt ); ::ScreenToClient( m_hWndOwner, &pt );
lParam = PtToLPARAM(pt);
::SendMessage( m_hWndOwner, uMsg, wParam, lParam );
}
bHandled = TRUE;
return 0;
}
};
// ----------------------------------------------------
inline LPARAM PtToLPARAM( POINT pt )
{
LPARAM lParam = 0;
WORD* pLParam = (WORD*)&lParam;
pLParam[0] = (short )pt.x;
pLParam[1] = (short )pt.y;
return lParam;
}
}
Думай, что хочешь, делай, что хочешь, живи, как хочешь -- всё тебе припомню!!
Re: WTL: Подсказка в любом месте экрана
Здравствуйте, WinterMute, Вы писали:
А в чем выражается нужда в виртуальном деструкторе ?
Еще хочу заметить что в WTL принято делать два класса : SomeClassImpl в котором вся функциональность и SomeClass производный от SomeClassImpl, где устанавливается класс окна через DECLARE_WND_SUPERCLASS.
Re[2]: WTL: Подсказка в любом месте экрана
__>А в чем выражается нужда в виртуальном деструкторе ?
Да ни в чём, убрать конечно же.
__>Еще хочу заметить что в WTL принято делать два класса : SomeClassImpl в котором вся функциональность и SomeClass производный от SomeClassImpl, где устанавливается класс окна через DECLARE_WND_SUPERCLASS.
Знаю, но в этом случае с трудом представляю зачем это может понадобиться.
Думай, что хочешь, делай, что хочешь, живи, как хочешь -- всё тебе припомню!!
Re[3]: WTL: Подсказка в любом месте экрана
Здравствуйте, WinterMute, Вы писали:
__>>А в чем выражается нужда в виртуальном деструкторе ?
WM>Да ни в чём, убрать конечно же.
__>>Еще хочу заметить что в WTL принято делать два класса : SomeClassImpl в котором вся функциональность и SomeClass производный от SomeClassImpl, где устанавливается класс окна через DECLARE_WND_SUPERCLASS.
WM>Знаю, но в этом случае с трудом представляю зачем это может понадобиться.
Для того чтобы кто-то смог в дальнейшем расширить функциональность класса.
Так принято в WTL и отходить от этого стиля не стоит.
Re[4]: WTL: Подсказка в любом месте экрана
Здравствуйте, _nn_, Вы писали:
WM>>Знаю, но в этом случае с трудом представляю зачем это может понадобиться.
__>Для того чтобы кто-то смог в дальнейшем расширить функциональность класса.
Да нафиг, и так нормально работает
. KISS
Думай, что хочешь, делай, что хочешь, живи, как хочешь -- всё тебе припомню!!
Re: WTL: Подсказка в любом месте экрана
Здравствуйте, WinterMute, Вы писали:
WM>
~Tooltip()
{
delete m_ToolInfo.lpszText; // << иначе утекает при уничтожении окна с текстом
m_ToolInfo.lpszText = NULL;
}
WM> ...
WM> CString GetText()
WM> {
WM> //::SendMessage( m_hWnd, TTM_GETTEXT, 0, m_ToolInfo ); << очипатка, должно быть
::SendMessage( m_hWnd, TTM_GETTEXT, 0, (LPARAM)& m_ToolInfo );
WM> return CString( m_ToolInfo.lpszText );
WM> }
WM>
Re: WTL: Подсказка в любом месте экрана
Здравствуйте, WinterMute, Вы писали:
::SendMessage(m_hWnd...)
В чем смысл в этм если можно писать просто SendMessage ?
Функцию PtToLPARAM слудует пересмотреть, можно это заменить одной строкой
Вдобавок лучше ее сделать статической функцией класса.
По возможности стоит избегать использования CString в классе или же через условную компиляцию добавлять функции.
А то не все пользуются CString и может вылезти ошибка.
Re[2]: WTL: Подсказка в любом месте экрана
Здравствуйте, _nn_, Вы писали:
__>Здравствуйте, WinterMute, Вы писали:
__>::SendMessage(m_hWnd...)
__>В чем смысл в этм если можно писать просто SendMessage ?
Как то пришлось переносить WTL-ные библиотеки на WinApi, теперь, если не особой разницы, я стараюсь пользоваться API функциями.
__>Функцию PtToLPARAM слудует пересмотреть, можно это заменить одной строкой
Как? Напиши пож, а то я сначала сделал одной строкой, дак она глючила.
__>Вдобавок лучше ее сделать статической функцией класса.
Мне она была нужна и в других классах, поэтому она глобальная.
__>По возможности стоит избегать использования CString в классе или же через условную компиляцию добавлять функции.
__>А то не все пользуются CString и может вылезти ошибка.
Приму во внимание.
Думай, что хочешь, делай, что хочешь, живи, как хочешь -- всё тебе припомню!!
Re[2]: WTL: Подсказка в любом месте экрана
Здравствуйте, Odi$$ey, Вы писали:
OE>Здравствуйте, WinterMute, Вы писали:
OE>
OE> ~Tooltip()
OE> {
OE> delete m_ToolInfo.lpszText; // << иначе утекает при уничтожении окна с текстом
OE> m_ToolInfo.lpszText = NULL;
OE> }
OE>
Вот это большое спасибо. У меня подсказка создавалась статически, поэтому наверное, и не заметил.
WM>> ...
WM>> CString GetText()
WM>> {
WM>> //::SendMessage( m_hWnd, TTM_GETTEXT, 0, m_ToolInfo ); << очипатка, должно быть
OE> ::SendMessage( m_hWnd, TTM_GETTEXT, 0, (LPARAM)& m_ToolInfo );
WM>> return CString( m_ToolInfo.lpszText );
WM>> }
Ага, добавил в последний момент, мне то она была не нужна.
Думай, что хочешь, делай, что хочешь, живи, как хочешь -- всё тебе припомню!!
Re[3]: WTL: Подсказка в любом месте экрана
Здравствуйте, WinterMute, Вы писали:
WM>Здравствуйте, _nn_, Вы писали:
__>>Здравствуйте, WinterMute, Вы писали:
__>>::SendMessage(m_hWnd...)
__>>В чем смысл в этм если можно писать просто SendMessage ?
WM>Как то пришлось переносить WTL-ные библиотеки на WinApi, теперь, если не особой разницы, я стараюсь пользоваться API функциями.
По CString это не заметно
А вообще все же не принято писать ::SendMessage(m_hWnd...)
Это выглядит мазохизмом, мы ведь в WTL пишем все же
__>>Функцию PtToLPARAM слудует пересмотреть, можно это заменить одной строкой
WM>Как? Напиши пож, а то я сначала сделал одной строкой, дак она глючила.
Ну попробую :
Оригинал :
inline LPARAM PtToLPARAM( POINT pt )
{
LPARAM lParam = 0;
WORD* pLParam = (WORD*)&lParam;
pLParam[0] = (short )pt.x;
pLParam[1] = (short )pt.y;
return lParam;
}
В младщую чать идет координата x, а в старшую часть идет координата y.
inline LPARAM PtToLPARAM(const POINT& pt)
{ return (LPARAM)(pt.x+pt.y<<16); }
Или же можно воспользоваться MAKEWORD
MAKEWORD(pt.x,pt.y)
И все
__>>Вдобавок лучше ее сделать статической функцией класса.
WM>Мне она была нужна и в других классах, поэтому она глобальная.
Понятно.
Re[4]: WTL: Подсказка в любом месте экрана
Здравствуйте, _nn_, Вы писали:
__>Здравствуйте, WinterMute, Вы писали:
WM>>Здравствуйте, _nn_, Вы писали:
__>>>Здравствуйте, WinterMute, Вы писали:
__>>>::SendMessage(m_hWnd...)
__>>>В чем смысл в этм если можно писать просто SendMessage ?
WM>>Как то пришлось переносить WTL-ные библиотеки на WinApi, теперь, если не особой разницы, я стараюсь пользоваться API функциями.
__>По CString это не заметно
Я СString тоже переносил, как-то хитро откусывая хвосты. И в случае чего опроедлить свой "CString" не проблема, там ведь всё что от СString используется это конструирование из константной строки и приведение к константной строке, такое из std::string в два счёта делается. А использовать строки без обёрток это как-то не по мне.
__>А вообще все же не принято писать ::SendMessage(m_hWnd...)
__>Это выглядит мазохизмом, мы ведь в WTL пишем все же
Правильно, я же мазохист
.
__>>>Функцию PtToLPARAM слудует пересмотреть, можно это заменить одной строкой
WM>>Как? Напиши пож, а то я сначала сделал одной строкой, дак она глючила.
__>Ну попробую :
__>Оригинал :
__>__> inline LPARAM PtToLPARAM( POINT pt )
__> {
__> LPARAM lParam = 0;
__> WORD* pLParam = (WORD*)&lParam;
__> pLParam[0] = (short )pt.x;
__> pLParam[1] = (short )pt.y;
__> return lParam;
__> }
__>
__>В младщую чать идет координата x, а в старшую часть идет координата y.
__>__>inline LPARAM PtToLPARAM(const POINT& pt)
__>{ return (LPARAM)(pt.x+pt.y<<16); }
__>
Это работает только если координаты положительны.
__>Или же можно воспользоваться MAKEWORD
__>MAKEWORD(pt.x,pt.y)
__>И все
Да, похоже на то, только MAKELONG.
Думай, что хочешь, делай, что хочешь, живи, как хочешь -- всё тебе припомню!!
Re[2]: Поправить конструктор
Здравствуйте, Odi$$ey, Вы писали:
WM>>
OE>
OE> ~Tooltip()
OE> {
OE> delete m_ToolInfo.lpszText; // << иначе утекает при уничтожении окна с текстом
OE> m_ToolInfo.lpszText = NULL;
OE> }
OE>
WM>>
Да, нужно еще поправить конструктор, иначе вызов деструктора объекта для которого не был применён метод Create приведёт к неопределённому поведению.
Правильный конструктор:
Tooltip()
: baseClass()
, m_bActive(false )
, m_hWndOwner(0)
{
memset(&m_ToolInfo, 0, sizeof (m_ToolInfo));
}
Думай, что хочешь, делай, что хочешь, живи, как хочешь -- всё тебе припомню!!
Итого: WTL: Подсказка в любом месте экрана
Всем спасибо за сделанные замечания. Вот поправленная версия:
namespace UI2
{
class Tooltip
: public CWindowImpl<Tooltip, ATL::CWindow>
{
public :
typedef CWindowImpl<Tooltip, ATL::CWindow> baseClass;
typedef Tooltip thisClass;
HWND m_hWndOwner;
private :
TOOLINFO m_ToolInfo;
bool m_bActive;
bool m_bShowAtCursor;
enum { TIMER_MSG_DELAY_SHOW = 100, TIMER_MSG_DELAY_HIDE = 101, };
static LPCTSTR GetWndClassName(){ return _T("Zaebis_Tooltip" ); }
public :
DECLARE_WND_SUPERCLASS(GetWndClassName(), baseClass::GetWndClassName())
BEGIN_MSG_MAP(thisClass)
MESSAGE_HANDLER(WM_TIMER, OnTimer)
MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
END_MSG_MAP()
Tooltip()
: baseClass()
, m_bActive(false )
, m_hWndOwner(0)
{
memset(&m_ToolInfo, 0, sizeof (m_ToolInfo));
}
~Tooltip()
{
delete m_ToolInfo.lpszText;
m_ToolInfo.lpszText = NULL;
}
bool Create( HWND hWndParent, HWND hWndOwner = NULL )
{
HWND hWnd = ::CreateWindowEx(WS_EX_TOPMOST,
TOOLTIPS_CLASS,
NULL,
TTS_NOPREFIX | TTS_ALWAYSTIP,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
hWndParent,
NULL,
NULL,
NULL);
ATLASSERT(hWnd);
// initialize toolinfo struct
memset(&m_ToolInfo, 0, sizeof (m_ToolInfo));
m_ToolInfo.cbSize = sizeof (m_ToolInfo);
m_ToolInfo.uFlags = TTF_TRACK | TTF_TRANSPARENT | TTF_SUBCLASS;
m_ToolInfo.hwnd = hWndParent;
m_hWndOwner = hWndOwner;
::SendMessage( hWnd, TTM_SETMAXTIPWIDTH, 0, SHRT_MAX);
::SendMessage( hWnd, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&m_ToolInfo);
SubclassWindow( hWnd );
return true ;
}
void SetBackColor( COLORREF c ){ ::SendMessage( m_hWnd, TTM_SETTIPBKCOLOR, c, 0 ); }
void SetTextColor( COLORREF c ){ ::SendMessage( m_hWnd, TTM_SETTIPTEXTCOLOR, c, 0 ); }
void SetMargins( const RECT* rectMargins ){ ::SendMessage(m_hWnd, TTM_SETMARGIN, 0, (LPARAM)rectMargins ); }
void SetMaxWidth( int maxWidth ){ ::SendMessage( m_hWnd, TTM_SETMAXTIPWIDTH, 0, (LPARAM)maxWidth ); }
bool IsVisible(){ return m_bActive; };
CString GetText()
{
::SendMessage( m_hWnd, TTM_GETTEXT, 0, (LPARAM)&m_ToolInfo );
return CString( m_ToolInfo.lpszText );
}
void SetText( const TCHAR* str )
{
delete m_ToolInfo.lpszText;
if ( str )
{
m_ToolInfo.lpszText = new TCHAR[ _tcslen(str)+1 ];
_tcscpy( m_ToolInfo.lpszText, str );
}
else m_ToolInfo.lpszText = NULL;
::SendMessage( m_hWnd, TTM_UPDATETIPTEXT, 0, (LPARAM)&m_ToolInfo );
}
void Move( int x, int y )
{
::SendMessage( m_hWnd, TTM_TRACKPOSITION, 0, (LPARAM)(DWORD)MAKELONG( x, y ) );
}
void Show( int x, int y )
{
Move( x, y );
Show();
}
void Show( const RECT* r )
{
m_bActive = true ;
m_ToolInfo.rect = *r;
::SendMessage( m_hWnd, TTM_TRACKPOSITION, 0, (LPARAM)(DWORD) MAKELONG( r->left, r->top ) );
::SendMessage( m_hWnd, TTM_SETMAXTIPWIDTH, 0, (LPARAM)(r->right - r->left) );
::SendMessage( m_hWnd, TTM_TRACKACTIVATE, TRUE, (LPARAM)&m_ToolInfo );
}
void Show( BOOL bShow = TRUE )
{
m_bActive = (bShow == TRUE);
::SendMessage( m_hWnd, TTM_TRACKACTIVATE, bShow, (LPARAM)&m_ToolInfo );
}
void ShowAtCursor()
{
POINT pt; ::GetCursorPos( &pt );
Show( pt.x, pt.y+20 );
}
void ShowAfter( UINT delay, bool bAtCursor = false )
{
if ( IsWindow() )
{
m_bShowAtCursor = bAtCursor;
KillTimer( TIMER_MSG_DELAY_SHOW );
SetTimer( TIMER_MSG_DELAY_SHOW, delay );
}
}
void ShowAtCursorAfter( UINT delay )
{
ShowAfter( delay, true );
}
void HideAfter( UINT delay )
{
if ( IsWindow() )
{
KillTimer( TIMER_MSG_DELAY_HIDE );
SetTimer( TIMER_MSG_DELAY_HIDE, delay );
}
}
void CancelShowAfter()
{
KillTimer( TIMER_MSG_DELAY_SHOW );
}
void CancelHideAfter()
{
KillTimer( TIMER_MSG_DELAY_HIDE );
}
operator HWND() { return m_hWnd; }
// Message handlers
LRESULT OnTimer( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
if ( TIMER_MSG_DELAY_SHOW == wParam )
{
m_bShowAtCursor?ShowAtCursor():Show();
KillTimer( wParam );
}
else if ( TIMER_MSG_DELAY_HIDE == wParam )
{
Show(FALSE);
KillTimer( wParam );
}
bHandled = FALSE;
return 0;
}
LRESULT OnMouseMessage( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
// forward message to owner window
if ( m_hWndOwner )
{
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
ClientToScreen( &pt ); ::ScreenToClient( m_hWndOwner, &pt );
::SendMessage( m_hWndOwner, uMsg, wParam, MAKELONG( pt.x, pt.y ) );
}
bHandled = TRUE;
return 0;
}
};
}
Думай, что хочешь, делай, что хочешь, живи, как хочешь -- всё тебе припомню!!
Re: Итого: WTL: Подсказка в любом месте экрана
Здравствуйте, WinterMute, Вы писали:
WM>Всем спасибо за сделанные замечания. Вот поправленная версия:
Еще поправка :
LPCTSTR GetText()
{
SendMessage(TTM_GETTEXT, 0, (LPARAM)&m_ToolInfo);
return m_ToolInfo.lpszText;
}
Кроме того что это избавляет от использования CString, это не вызвает лишний конструктор.
Re[2]: Итого: WTL: Подсказка в любом месте экрана
__>Еще поправка :
__>__>LPCTSTR GetText()
__>{
__> SendMessage(TTM_GETTEXT, 0, (LPARAM)&m_ToolInfo);
__> return m_ToolInfo.lpszText;
__>}
__>
__>Кроме того что это избавляет от использования CString, это не вызвает лишний конструктор.
Ты прав, константный указатель на строку лучше.
Думай, что хочешь, делай, что хочешь, живи, как хочешь -- всё тебе припомню!!
Re[5]: WTL: Подсказка в любом месте экрана
Здравствуйте, WinterMute, Вы писали:
WM>Это работает только если координаты положительны.
WM>Да, похоже на то, только MAKELONG.
Конечно
Так устроит ?
MAKELONG((short )pt.x,(short )pt.y)
Re[6]: WTL: Подсказка в любом месте экрана
__>Так устроит ?
__>__>MAKELONG((short )pt.x,(short )pt.y)
__>
Устроит. Но приведение к "short" лишнее, в макросе MAKELONG все компоненты усекаются по маске 0xffff.
Думай, что хочешь, делай, что хочешь, живи, как хочешь -- всё тебе припомню!!
Re: Итого: WTL: Подсказка в любом месте экрана
Здравствуйте, WinterMute, Вы писали:
WM>Всем спасибо за сделанные замечания. Вот поправленная версия:
Еще маленькое замечание — раз память выделяется по new [], то и удалять надо по delete [], а не по delete
Re[2]: Итого: WTL: Подсказка в любом месте экрана
AG>Еще маленькое замечание — раз память выделяется по new [], то и удалять надо по delete [], а не по delete
Да, я тоже недавно заметил
.
Думай, что хочешь, делай, что хочешь, живи, как хочешь -- всё тебе припомню!!
Итого2: WTL: Подсказка в любом месте экрана
Еще несколько маленьких доработок:
namespace UI2
{
class Tooltip
: public CWindowImpl<Tooltip, ATL::CWindow>
{
public :
typedef CWindowImpl<Tooltip, ATL::CWindow> baseClass;
typedef Tooltip thisClass;
HWND m_hWndOwner;
private :
TOOLINFO m_ToolInfo;
bool m_bVisible;
bool m_bShowAtCursor;
enum { TIMER_MSG_DELAY_SHOW = 100, TIMER_MSG_DELAY_HIDE = 101, };
static LPCTSTR GetWndClassName(){ return _T("Zaebis_Tooltip" ); }
public :
DECLARE_WND_SUPERCLASS(GetWndClassName(), baseClass::GetWndClassName())
BEGIN_MSG_MAP(thisClass)
MESSAGE_HANDLER(WM_TIMER, OnTimer)
MESSAGE_HANDLER(WM_SETCURSOR, OnMouseMessage) // Если хочется что бы над подказкой был такой-же курсор как и над хозяином
MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
END_MSG_MAP()
Tooltip()
: baseClass()
, m_bVisible(false )
, m_hWndOwner(0)
{
memset(&m_ToolInfo, 0, sizeof (m_ToolInfo));
}
~Tooltip()
{
DestroyWindow();
}
bool Create( HWND hWndParent, HWND hWndOwner = NULL )
{
DestroyWindow();
HWND hWnd = ::CreateWindowEx(WS_EX_TOPMOST,
TOOLTIPS_CLASS,
NULL,
TTS_NOPREFIX | TTS_ALWAYSTIP,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
hWndParent,
NULL,
NULL,
NULL);
ATLASSERT(hWnd);
// initialize toolinfo struct
m_ToolInfo.cbSize = sizeof (m_ToolInfo);
m_ToolInfo.uFlags = TTF_TRACK | TTF_TRANSPARENT | TTF_SUBCLASS;
m_ToolInfo.hwnd = hWndParent;
m_hWndOwner = hWndOwner;
::SendMessage( hWnd, TTM_SETMAXTIPWIDTH, 0, SHRT_MAX );
::SendMessage( hWnd, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&m_ToolInfo );
SubclassWindow( hWnd );
return true ;
}
void DestroyWindow()
{
if ( m_hWnd )baseClass::DestroyWindow();
delete [] m_ToolInfo.lpszText;
memset(&m_ToolInfo, 0, sizeof (m_ToolInfo));
}
void SetBackColor( COLORREF c ){ ::SendMessage( m_hWnd, TTM_SETTIPBKCOLOR, c, 0 ); }
void SetTextColor( COLORREF c ){ ::SendMessage( m_hWnd, TTM_SETTIPTEXTCOLOR, c, 0 ); }
void SetMargins( const RECT* rectMargins ){ ::SendMessage(m_hWnd, TTM_SETMARGIN, 0, (LPARAM)rectMargins); }
void SetMaxWidth( int maxWidth ){ ::SendMessage( m_hWnd, TTM_SETMAXTIPWIDTH, 0, (LPARAM)maxWidth ); }
bool IsVisible(){ return m_bVisible; };
const TCHAR* GetText()
{
::SendMessage( m_hWnd, TTM_GETTEXT, 0, (LPARAM)&m_ToolInfo );
return m_ToolInfo.lpszText;
}
void SetText( const TCHAR* text )
{
delete [] m_ToolInfo.lpszText;
if ( text )
{
m_ToolInfo.lpszText = new TCHAR[ _tcslen(text)+1 ];
_tcscpy( m_ToolInfo.lpszText, text );
}
else m_ToolInfo.lpszText = NULL;
::SendMessage( m_hWnd, TTM_UPDATETIPTEXT, 0, (LPARAM)&m_ToolInfo );
}
void Move( int x, int y )
{
::SendMessage( m_hWnd, TTM_TRACKPOSITION, 0, (LPARAM)(DWORD)MAKELONG( x, y ) );
}
void Show( int x, int y )
{
Move( x, y );
Show();
}
void Show( const RECT* r )
{
m_bVisible = true ;
m_ToolInfo.rect = *r;
::SendMessage( m_hWnd, TTM_TRACKPOSITION, 0, (LPARAM)(DWORD) MAKELONG( r->left, r->top ) );
::SendMessage( m_hWnd, TTM_SETMAXTIPWIDTH, 0, (LPARAM)(r->right - r->left) );
::SendMessage( m_hWnd, TTM_TRACKACTIVATE, TRUE, (LPARAM)&m_ToolInfo );
}
void Show( BOOL bShow = TRUE )
{
m_bVisible = (bShow == TRUE);
::SendMessage( m_hWnd, TTM_TRACKACTIVATE, bShow, (LPARAM)&m_ToolInfo );
}
void ShowAtCursor()
{
POINT pt; ::GetCursorPos( &pt );
Show( pt.x, pt.y+20 );
}
void ShowAfter( UINT delay, bool bAtCursor = false )
{
if ( IsWindow() )
{
m_bShowAtCursor = bAtCursor;
KillTimer( TIMER_MSG_DELAY_SHOW );
SetTimer( TIMER_MSG_DELAY_SHOW, delay );
}
}
void ShowAtCursorAfter( UINT delay )
{
ShowAfter( delay, true );
}
void HideAfter( UINT delay )
{
if ( IsWindow() )
{
KillTimer( TIMER_MSG_DELAY_HIDE );
SetTimer( TIMER_MSG_DELAY_HIDE, delay );
}
}
void CancelShowAfter()
{
KillTimer( TIMER_MSG_DELAY_SHOW );
}
void CancelHideAfter()
{
KillTimer( TIMER_MSG_DELAY_HIDE );
}
operator HWND() { return m_hWnd; }
// Message handlers
LRESULT OnTimer( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
if ( TIMER_MSG_DELAY_SHOW == wParam )
{
m_bShowAtCursor?ShowAtCursor():Show();
KillTimer( wParam );
}
else if ( TIMER_MSG_DELAY_HIDE == wParam )
{
Show(FALSE);
KillTimer( wParam );
}
bHandled = FALSE;
return 0;
}
LRESULT OnMouseMessage( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
LRESULT ret = 0;
// forward message to owner window
if ( m_hWndOwner )
{
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
ClientToScreen( &pt ); ::ScreenToClient( m_hWndOwner, &pt );
ret = ::SendMessage( m_hWndOwner, uMsg, wParam, MAKELONG( pt.x, pt.y ) );
}
//bHandled = TRUE;
bHandled = FALSE;
return ret;
}
};
}
... << RSDN@Home 1.1.4 @@subversion >>
Пока на собственное сообщение не было ответов, его можно удалить.
Удалить