DuiLib事件分析(一)——鼠标事件响应

最近在处理DuiLib中自定义列表行元素事件,因为处理方案得不到较好的效果,于是只好一层一层的去剥离DuiLib事件是怎么来的,看能否在某一层截取消息,自己重写。

我这里使用CListContainerElementUI行元素,元素中有插入button,平时行元素不显示,鼠标移动上去显示出来,鼠标移走就隐藏button。Duilib自己是不带这个功能的,它有一个鼠标移动上去的热点事件,按理说重写热点事件就好了。但是当时比较急没找到怎么触发的,之后一直没继续走这条思路。后来找到源码事件里面有

void CListContainerElementUI::DoEvent(TEventUI& event)

if( event.Type == UIEVENT_MOUSEENTER )
{
    if( IsEnabled() ) {
    m_uButtonState |= UISTATE_HOT;
    Invalidate();

   //我自己添加的回调函数
   pCallBackFunc(this);

    }
    return;
}

于是就在UIEVENT_MOUSEENTER中加入了自己写的一个回调函数,只要行元素触发鼠标移动上去的事件,就去调用回调,回调再去根据业务逻辑操作UI。想法很好,但是遇到一个问题,当鼠标移动到行元素的button上时,行元素会触发

if( event.Type == UIEVENT_MOUSELEAVE )
{
    if( (m_uButtonState & UISTATE_HOT) != 0 ) {
        m_uButtonState &= ~UISTATE_HOT;
        Invalidate();
    }
    return;
}

所以,如果在UIEVENT_MOUSEENTER时去设置为移动上行元素效果,在UIEVENT_MOUSELEAVE时设置为离开行元素效果,行不通。

当时设置了一个偷懒的方式,当鼠标移动到另外一个行元素上时,重置其他行元素的UI为鼠标离开。看似这样就解决问题了,但是当鼠标不移动到其他行元素上,直接离开列表时,则会导致鼠标最后停留的那个行元素不能被置为鼠标离开状态。

通过对相关源码的阅读,得到以下关系图:

而CControlUI并不继承自任何类。

class UILIB_API CControlUI
{
public:
    CControlUI();
    virtual ~CControlUI();
......
}

同时,在CControlUI中,DoEvent是来自于以下调用

void CControlUI::Event(TEventUI& event)
{
    if( OnEvent(&event) ) DoEvent(event);
}

查看Event引用得到下图:

前面提到CControlUI不继承自任何类,显然Event不是重构了一个系统函数,调用必然不会来自于上图的后面4条,而1,2条是定义和实现。这条线索算是中断了。

于是,现在想从行元素鼠标离开事件本身着手,看能否从堆栈跟踪里面得到答案。在UIEVENT_MOUSELEAVE处断点,跟踪堆栈:

这样我们就看到了是来自于以下调用

> dclient.exe!DuiLib::CPaintManagerUI::MessageHandler(unsigned int uMsg, unsigned int wParam, long lParam, long & lRes) 行 843 + 0x21 字节 C++

我们再来看看MessangeHandler的代码

bool CPaintManagerUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes)
{
    ......
    switch( uMsg ) {
    .....
    case WM_CLOSE:...
    case WM_SIZE:...
    case WM_MOUSEHOVER:...
    case WM_MOUSELEAVE:...
    case WM_MOUSEMOVE:...
    ......
}

也就是说,消息会在这里被默认处理,而它的再上一层调用是用户自己定义的消息处理机制。我们此时再来看自己写的调用:

//消息循环
LRESULT MyFrameWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
	{
        ......
	case WM_CLOSE:...
        ......
	default:
		break;
	}
	if( bHandled ) return lRes;
	return WindowImplBase::HandleMessage(uMsg, wParam, lParam);
}

这里的return就是把消息返回给了WindowImplBase

官方示例文档Duilib入门是返回给了

return CWindowWnd::HandleMessage(uMsg, wParam, lParam);

这两个有些差异,但是并不影响我们继续分析,我们的WindowImpIBase的调用就是来自于

LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CWindowWnd* pThis = NULL;
    ......
    pThis = reinterpret_cast<CWindowWnd*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA))
    .....
        if( pThis != NULL ) {
        return pThis->HandleMessage(uMsg, wParam, lParam);
    }
    else {
        return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
}

可见所有事件是来自于CWindowWnd窗口,窗口直接把事件给了开发者,当开发者不拦截相应事件的时候,就return给了DuiLib处理。DuiLib通过层层机制,反馈给相应的控件去处理事件。

所有,要实现自定义消息处理,不用动源码,只要直接在HandleMessage中拦截消息即可。

OK,看似思路清晰了,但是还面临一个问题,给开发者调用的事件里面,没有参数表明是哪个元素触发的,想从这个层面拦截,似乎只能拦截一些通用事件,比如关闭,比如按钮点击。

我们来看看UIManager中,MessageHandler在处理鼠标移动时的处理方法 case WM_MOUSEMOVE:

            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 ? 400UL : (DWORD) ::SendMessage(m_hwndTooltip, TTM_GETDELAYTIME, TTDT_INITIAL, 0L);
                _TrackMouseEvent(&tme);
                m_bMouseTracking = true;
            }
            // Generate the appropriate mouse messages            //获取鼠标当前位置
            POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
            m_ptLastMousePos = pt;            //根据位置获取鼠标在某个元素上
            CControlUI* pNewHover = FindControl(pt);
            if( pNewHover != NULL && pNewHover->GetManager() != this ) break;
            TEventUI event = { 0 };
            event.ptMouse = pt;
            event.dwTimestamp = ::GetTickCount();            //判断是否鼠标离开某个元素
            if( pNewHover != m_pEventHover && m_pEventHover != NULL ) {
                event.Type = UIEVENT_MOUSELEAVE;
                event.pSender = m_pEventHover;                //这里的m_pEventHover经过后面一些代码运作,就会被赋值为pNewHover,也就是鼠标移动上去的那个元素
                m_pEventHover->Event(event);
                m_pEventHover = NULL;
                if( m_hwndTooltip != NULL ) ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, FALSE, (LPARAM) &m_ToolTip);
            }            //判断鼠标是否进入某个元素
            if( pNewHover != m_pEventHover && pNewHover != NULL ) {
                event.Type = UIEVENT_MOUSEENTER;
                event.pSender = pNewHover;
                pNewHover->Event(event);                //设置热点元素为当前元素
                m_pEventHover = pNewHover;
            }
            if( m_pEventClick != NULL ) {
                event.Type = UIEVENT_MOUSEMOVE;
                event.pSender = m_pEventClick;
                m_pEventClick->Event(event);
            }
            else if( pNewHover != NULL ) {
                event.Type = UIEVENT_MOUSEMOVE;                event.pSender = pNewHover;
                pNewHover->Event(event);
            }
        }

  先通过鼠标位置判断是否在某个元素上,在元素上则标记m_pEventHover为当前元素,再去配置相关消息,调用元素的Event。也就是说,在DuiLib处理鼠标移动事件时,根据鼠标位置获取了相应元素,并通触发该元素行为。由此可见,自定义列表行元素,当鼠标停留在行元素上时,会触发UIEVENT_MOUSEENTER,而当鼠标移动到行元素的button上时,会修改m_pEventHover,它会认为这是移动到另外一个元素上了,去调用这个新元素的Event。

到我们可以整理出DuiLib鼠标事件响应的大概流程:

1.窗体触发事件

2.用户消息循环,把处理不掉的信息反馈给DuiLib的基类处理,基类通过CPaintManagerUI的MessageHandler来处理一些基础消息

3.MessageHandler把消息分类处理

根据以上分析,如果要实现我前面提到的鼠标移动上行元素,再改变行元素状态,要通过修改DuiLib源码来实现就非常困难了。MessageHandler是无法预知,也不应该知道用户层面想要的特效的。所以最终要解决这个问题,还是应该放到用户消息循环,通过比较复杂的逻辑去实现。

  

DuiLib事件分析(一)——鼠标事件响应

时间: 2024-11-10 00:10:07

DuiLib事件分析(一)——鼠标事件响应的相关文章

JavaScript的事件对象_鼠标事件

鼠标事件是 Web 上面最常用的一类事件,毕竟鼠标还是最主要的定位设备.那么通过事件对象可以获取到鼠标按钮信息和屏幕坐标获取等. 一.鼠标按钮 只有在主鼠标按钮被单击时(常规一般是鼠标左键)才会触发 click 事件,因此检测按钮的信息并不是必要的. 但对于 mousedown 和 mouseup 事件来说,则在其 event 对象存在一个 button 属性,表示按下或释放按钮. <script type="text/javascript"> window.onload

javaScript事件(五)事件类型之鼠标事件

一.事件 二.事件流 以上内容见:javaScript事件(一)事件流 三.事件处理程序 四.IE事件处理程序 以上内容见javaScript事件(二)事件处理程序 五.事件对象 以上内容见javaScript事件(三)事件对象 六.事件对象的公共成员 以上内容见javaScript事件(四)event的公共成员 七.鼠标事件 DOM3级事件中定义了9个鼠标事件. mousedown:鼠标按钮被按下(左键或者右键)时触发.不能通过键盘触发. mouseup:鼠标按钮被释放弹起时触发.不能通过键盘

JS——事件详情(鼠标事件)

鼠标位置 1>可视区位置:clientX.clientY 跟着鼠标移动的div案例 代码如下图:   这个案例,运用到前一篇文章中的event事件来处理.获取div的left和top值,当鼠标移动时,div的left和top值跟着鼠标位置改变而改变.(注意的是:需要给div设置绝对定位) 演示效果如下图: 但是!!! 当我给body设置高度时,改变body的高度,这个案例效果就发生变化了,变得很诡异. 代码如下图: 效果图显示如下: 为什么给body设置了一个2000px的高度后,这个案例就变得

鼠标事件(jQuery)

1jQuery鼠标事件之click与dbclick事件 用交互操作中,最简单直接的操作就是点击操作.jQuery提供了两个方法一个是click方法用于监听用户单击操作,另一个方法是dbclick方法用于监听用户双击操作.这两个方法的用法是类似的,下面以click()事件为例 使用上非常简单: 方法一:$ele.click() 绑定$ele元素,不带任何参数一般是用来指定触发一个事件,用的比较少 <div id="test">点击触发<div> $("e

JS事件(7)——事件类型——鼠标与滚轮事件

鼠标与滚轮事件 鼠标事件 “DOM3级事件”中定义了9个鼠标事件. click:在单击主鼠标按钮(一般是左键)或者按下回车时触发:这意味着onclick事件处理程序既可以通过鼠标也可以通过键盘执行. dbclick:双击主鼠标按钮(一般是左键)或者按下回车键时触发. mousedown:按下任意鼠标按钮时触发:不能通过键盘触发. mouseup:释放鼠标按钮时触发:不能通过键盘触发. mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发:这个事件不冒泡,而且光标移动到元素的后代

JavaScript 鼠标事件大全

一般事件 事件 浏览器支持 描述 onClick HTML: 2 | 3 | 3.2 | 4 Browser: IE3 | N2 | O3 鼠标点击事件,多用在某个对象控制的范围内的鼠标点击 onDblClick HTML: 2 | 3 | 3.2 | 4 Browser: IE4 | N4 | O 鼠标双击事件 onMouseDown HTML: 2 | 3 | 3.2 | 4 Browser: IE4 | N4 | O 鼠标上的按钮被按下了 onMouseUp HTML: 2 | 3 | 3

selenium + python 鼠标事件

selenium + python 鼠标事件 十.鼠标事件 本章重点: ActionChains 类 ? context_click() 右击 ? double_click() 双击 ? drag_and_drop() 拖动 测试的产品中有一个操作是右键点击文件列表会弹出一个快捷菜单,可以方 便的选择快捷菜单中的选择对文件进行操作(删除.移动.重命名),之前学习 元素的点击非常简单: driver.find_element_by_id(“xxx”).click() 博客园—虫师 http://f

JavaSE 鼠标事件类(MouseEvent)实现

{相关信息}鼠标事件类(MouseEvent)指组件中发生的鼠标动作事件,例如按下鼠标.释放鼠标.单击鼠标.鼠标光标进入或离开组件的几何 图形.移动鼠标.拖动鼠标.当鼠标移动到某个区域或鼠标单击某个组件时就会触发鼠标事件.使用鼠标事件必须给组件添加一个MouseListener 接口的事件处理器,该接口包含以下 5 个方法:void mouseClicked(MouseEvent e):当鼠标在该区域单击时发生void mouseEntered(MouseEvent e):当鼠标进入该区域时发生

javaScript事件(八)事件类型之变动事件

DOM2级的变动(mutation)事件能在DOM中某一部分发送变化时给出提示.变动事件为XML或HTML DOM设计的,并不特定于某种语言.DOM2级定义了如下变动事件. DOMSubtreeModifined:在DOM结构发生任何变化的时候.这个事件在其他事件触发后都会触发. DOMNodeInserted:当一个节点作为子节点被插入到另一个节点中时触发. DOMNodeRemoved:在节点从其父节点中移除时触发. DOMNodeInsertedIntoDocument:在一个节点被直接插

javaScript事件(九)事件类型之触摸与手势事件

一.触摸事件 touchstart:当手指触摸屏幕时触发:即使已经有一个手指放在了屏幕上也会触发. touchmove:当手指在屏幕上滑动时连续地触发.在这个世界发生期间,调用preventDefault()可以阻止滚动. touchend:当手指在屏幕上移开时触发. touchcancel:当系统停止跟踪触摸时触发.关于此事件的确切触发时间,文档中没有明确说明. 上面这几个事件都会冒泡,也都可以取消.虽然这些触摸事件没有在DOM规范中定义,但它们却是以兼容DOM的方式实现的.因此,每个触摸事件