Duilib 源码分析之 Timer 篇

Duilib 中的 Timer 是封装了 Windows 的 Timer, 将事件绑定到了具体的控件上。
相关逻辑实现代码如下:

  • 内部 Timer 的数据结构

    typedef struct tagTIMERINFO
    {
    CControlUI* pSender;
    UINT nLocalID;
    HWND hWnd;
    UINT uWinTimer;
    bool bKilled;
    } TIMERINFO;

    pSender -> 当前 Timer 绑定的控件,当 Timer 的间隔时间到了之后会发送 UIEVENT_TIMER 事件给当前控件
    nLocalID -> 当前 Timer 的逻辑 id,并不是传入到 ::Setimer 中的 id,而是调用 bool CPaintManagerUI::SetTimer(CControlUI* pControl, UINT nTimerID, UINT uElapse) 时的第 2 个参数
    hWnd -> 当前 Timer 对应的窗口句柄,是实际传入到 ::SetTimer 中的参数
    uWinTimer -> 实际的 Timer 的 Id
    bKilled -> 当前 Timer 是否已无效,调用 bool KillTimer(CControlUI* pControl, UINT nTimerID) 后会将对应的 Time 杀掉

  • SetTimer 的实现

    bool CPaintManagerUI::SetTimer(CControlUI* pControl, UINT nTimerID, UINT uElapse)
    {
    ASSERT(pControl!=NULL);
    ASSERT(uElapse>0);
    for( int i = 0; i< m_aTimers.GetSize(); i++ ) {
        TIMERINFO* pTimer = static_cast<TIMERINFO*>(m_aTimers[i]);
        if( pTimer->pSender == pControl
            && pTimer->hWnd == m_hWndPaint
            && pTimer->nLocalID == nTimerID ) {
            if( pTimer->bKilled == true ) {
                if( ::SetTimer(m_hWndPaint, pTimer->uWinTimer, uElapse, NULL) ) {
                    pTimer->bKilled = false;
                    return true;
                }
                return false;
            }
            return false;
        }
    }
    
    m_uTimerID = (++m_uTimerID) % 0xFF;
    if( !::SetTimer(m_hWndPaint, m_uTimerID, uElapse, NULL) ) return FALSE;
    TIMERINFO* pTimer = new TIMERINFO;
    if( pTimer == NULL ) return FALSE;
    pTimer->hWnd = m_hWndPaint;
    pTimer->pSender = pControl;
    pTimer->nLocalID = nTimerID;
    pTimer->uWinTimer = m_uTimerID;
    pTimer->bKilled = false;
    return m_aTimers.Add(pTimer);
    }

    CPainterManagerUI 中的 CDuiPtrArray m_aTimers 保存了所有的 TIMERINFO 对象, 每调用一次 SetTimer 就会从既存 m_aTimers 中查找相同的 TIMERINFO, 若找到且已被杀掉,则重启开启此 Timer,否则忽略当前 SetTimer 的调用。 这一点和 Windows 的默认 ::SetTimer 有区别, Windows 的 ::SetTimer 会覆盖原始 Timer 且重新计时

  • KillTimer的实现

    bool CPaintManagerUI::KillTimer(CControlUI* pControl, UINT nTimerID)
    {
        ASSERT(pControl!=NULL);
        for( int i = 0; i< m_aTimers.GetSize(); i++ ) {
            TIMERINFO* pTimer = static_cast<TIMERINFO*>(m_aTimers[i]);
            if( pTimer->pSender == pControl
                && pTimer->hWnd == m_hWndPaint
                && pTimer->nLocalID == nTimerID )
            {
                if( pTimer->bKilled == false ) {
                    if( ::IsWindow(m_hWndPaint) ) ::KillTimer(pTimer->hWnd, pTimer->uWinTimer);
                    pTimer->bKilled = true;
                    return true;
                }
            }
        }
        return false;
    }

    调用了 KillTimer 之后对应的 TIMERINFObKilled 设置为 true, 且调用了 ::KillTimer 杀掉了实际的 Timer,但 TIMERINFO 对象未析构,在调用 SetTimer 时有可能会重新激活此对象,并将 bKilled 设置为 false

  • CPaintManagerUI 中的 WM_TIMER 处理:

    case WM_TIMER:
    {
        for (int i = 0; i < m_aTimers.GetSize(); i++) {
            const TIMERINFO* pTimer = static_cast<TIMERINFO*>(m_aTimers[i]);
            if (pTimer->hWnd == m_hWndPaint && pTimer->uWinTimer == wParam && pTimer->bKilled == false) {
                TEventUI event = { 0 };
                event.Type = UIEVENT_TIMER;
                event.pSender = pTimer->pSender;
                event.wParam = pTimer->nLocalID;
                event.dwTimestamp = ::GetTickCount();
                pTimer->pSender->Event(event);
                break;
            }
        }
    }

    根据 wParam 中的 TimerID 查找对应的 TIMERINFO 对象,然后向绑定的控件发送 UIEVENT_TIMER 事件进行处理。 像 CGifAnimUI CScrollBarUI 等会自定义对 UIEVENT_TIMER 事件的响应,否则则会将事件默认发送给监听者

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值