Duilib 源码分析之 ToolTip 篇

首先关于 ToolTip 的相关资料,建议先看一下百度百科的介绍:TOOLINFO

Duilib 中对 Tooltip 的用法还算是比较简单易懂的,而且实现方法也很简单,只需要为控件添加属性 tooltip , 值是希望提示的字符串就可以了,当鼠标移动到控件上停留时,会发送 WM_MOUSEHOVER 消息,Duilib 此时就会开始显示 tooltip 窗口了。 接下来看一下实现 tooltip 的代码:

bool CPaintManagerUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes)
{
  ......
  case WM_MOUSEHOVER:
    ......
    // Create tooltip information
    CDuiString sToolTip = pHover->GetToolTip();
    if( sToolTip.IsEmpty() ) return true;
    ::ZeroMemory(&m_ToolTip, sizeof(TOOLINFO));
    m_ToolTip.cbSize = sizeof(TOOLINFO);
    m_ToolTip.uFlags = TTF_IDISHWND;
    m_ToolTip.hwnd = m_hWndPaint;
    m_ToolTip.uId = (UINT_PTR) m_hWndPaint;
    m_ToolTip.hinst = m_hInstance;
    m_ToolTip.lpszText = const_cast<LPTSTR>( (LPCTSTR) sToolTip );
    m_ToolTip.rect = pHover->GetPos();
    if( m_hwndTooltip == NULL ) {
        m_hwndTooltip = ::CreateWindowEx(0, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, m_hWndPaint, NULL, m_hInstance, NULL);
    ::SendMessage(m_hwndTooltip, TTM_ADDTOOL, 0, (LPARAM) &m_ToolTip);                  ::SendMessage(m_hwndTooltip,TTM_SETMAXTIPWIDTH,0, pHover->GetToolTipWidth());
    }
    if(!::IsWindowVisible(m_hwndTooltip))
    {
        ::SendMessage(m_hwndTooltip, TTM_SETTOOLINFO, 0, (LPARAM)&m_ToolTip);
        ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, TRUE, (LPARAM)&m_ToolTip);
    }

   ......
   case WM_MOUSELEAVE:
    {
    if( m_hwndTooltip != NULL ) ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, FALSE, (LPARAM) &m_ToolTip);
    .....
    }
}

以上代码段是创建、显示、隐藏 Tooltip 窗口的代码。从代码可以看出,一个窗口只创建了一个 Tooltip 的窗口,不论鼠标移动到哪个子控件上显示 Tooltip,其实都是显示的同一个 Windows 窗口,只不过会修改其中的文字而已

  • CreateWindowEx 创建一个 ToolTip 窗口
  • SendMessage(m_hwndTooltip, TTM_ADDTOOL, 0, (LPARAM) &m_ToolTip) 将 Tooltip 的数据设置到当前 ToolTip 窗口中
  • ::SendMessage(m_hwndTooltip,TTM_SETMAXTIPWIDTH,0, pHover->GetToolTipWidth()) 设置 ToolTip 窗口的宽度,我看了下 Duilib 中目前 m_nTooltipWidth 默认为 300,而且没有对应的属性可以修改。我建议可以添加一个 tooltipwidth 的属性,解析到此属性后调用已存在的方法:void SetToolTipWidth(int nWidth)
  • ::SendMessage(m_hwndTooltip, TTM_SETTOOLINFO, 0, (LPARAM)&m_ToolTip) 这行代码的作用其实是想改变 Tooltip 窗口中的文字,而且是在 Tooltip 隐藏的情况下,因为此时我们有可能是从前一个控件移动到下一个控件,此时需要重新显示 Tooltip 窗口并修改文字。控制 Tooltip 窗口的隐藏显示需要用到下面的 2 行代码
  • ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, TRUE, (LPARAM)&m_ToolTip) 显示 Tooltip 窗口
  • ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, FALSE, (LPARAM) &m_ToolTip) 隐藏 Tooltip 窗口

接下来,如果我们在 Duilib 中搜索一下 m_hwndTooltip 的出现场景,除了上面提到的场景和窗口关闭时销毁窗口外,还有一个地方用到了 m_hwndTooltip :

case WM_MOUSEMOVE:
{
    if( m_pRoot == NULL ) break;
    // Start tracking this entire window again...
    if( !m_bMouseTracking ) {
        TRACKMOUSEEVENT tme = { 0 };
        tme.cbSize = sizeof(TRACKMOUSEEVENT);
        tme.dwFlags = TME_HOVER | TME_LEAVE;
        tme.hwndTrack = m_hWndPaint;
        tme.dwHoverTime = m_hwndTooltip == NULL ? m_iHoverTime : (DWORD) ::SendMessage(m_hwndTooltip, TTM_GETDELAYTIME, TTDT_INITIAL, 0L);
        _TrackMouseEvent(&tme);
        m_bMouseTracking = true;
    }
}

这里用到 了一个 API : BOOL TrackMouseEvent(LPTRACKMOUSEEVENT lpEventTrack) , 这个函数可以寄送 WM_MOUSEHOVER WM_MOUSELEAVE 两个消息, 默认情况下 Windows 是不会向窗口发送这两个消息的,需要调用 TrackMouseEvent 来实现。 RACKMOUSEEVENT 的定义如下:

typedef struct tagTRACKMOUSEEVENT {
    DWORD cbSize;
    DWORD dwFlags;
    HWND  hwndTrack;
    DWORD dwHoverTime;
} TRACKMOUSEEVENT, *LPTRACKMOUSEEVENT;
  • cbSize -> 直接设置为 sizeof(TRACKMOUSEEVENT)即可
  • dwFlags ->代表 TrackMouseEvent 寄送的消息类型, TME_HOVER TME_LEAVE 分别对应 WM_MOUSEHOVER WM_MOUSELEAVE,也可以两个都写上: TME_HOVER | TME_LEAVE
  • hwndTrack -> 寄送消息的目的窗口
  • dwHoverTime -> 发送 WM_MOUSEHOVER 消息前鼠标所需保持停止不动的时间

每次 TrackMouseEvent 调用并发送消息后即失效,若想再次接收 WM_MOUSEHOVER WM_MOUSELEAVE 需要再次调用 TrackMouseEvent 。这部分实现可以看一下 CPaintManagerUI::m_bMouseTracking 的赋值情况,每次 WM_MOUSEHOVER WM_MOUSELEAVE 处理后即设置为 false, 且在 WM_MOUSEMOVE 时发现为 false,会重新调用 TrackMouseEvent

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值