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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
duilib是一个开的C++图形界面库,它提供了丰富的控件和布局管理功能,以及良好的跨平台支持。下面我将对duilib码进行简要分析。 1. 基础架构:duilib码采用了模块化的设计,主要分为“Core”和“UI”两个部分。其中,“Core”模块是duilib的核心部分,提供了基础的窗口、消息循环等功能;而“UI”模块则提供了各种控件和布局管理等高级功能。 2. 控件类别:duilib提供了丰富的控件类别,包括基础的窗口类(如窗口、对话框)、容器类(如水平布局、垂直布局)、常用控件类(如按钮、文本框)、自定义控件类等。每个控件类都有相应的成员函数和消息处理函数,以便实现对控件的创建、设置属性和处理事件等操作。 3. 消息处理机制:duilib使用了消息映射的机制来处理控件的事件。每个控件类都有自己的消息映射表,用于将消息和相应的处理函数关联起来。当控件接收到特定的消息时,duilib会根据映射表找到对应的处理函数进行处理。 4. 布局管理:duilib提供了灵活且强大的布局管理功能,可以通过设置布局属性实现控件的自动适应和自动排列。布局管理器可以根据指定的规则对子控件进行自动布局,以适应不同的窗口尺寸。 5. 绘制引擎:duilib使用了自定义的绘制引擎来实现界面绘制。该绘制引擎可以根据控件的属性和状态来决定绘制的方式,以实现不同的视觉效果。 总结来说,duilib分析涉及到基础架构、控件类别、消息处理机制、布局管理和绘制引擎等方面。通过深入研究这些内容,我们可以更好地理解duilib的设计理念和工作原理,以便能够更好地使用和定制duilib提供的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值