1 Duilib原有列表子项鼠标点击消息的弊端

Duilib中原有的列表控件UIList中的子项CListContainerElementUICListLabelElementUI的鼠标左键消息和右键消息全部都是发送的DUI_MSGTYPE_ITEMCLICK消息,也就是说我们只能知道列表项被点击,并不能分清是左键点击还是右键点击,这在源码中也有体现,比如

if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_RBUTTONDOWN )
{
    if( IsEnabled() ) {
        m_pManager->SendNotify(this, DUI_MSGTYPE_ITEMCLICK);
        Select();
        Invalidate();
    }
    return;
}

在现实的开发需求中,我们经常需要对列表中的子项进行操作,而这就经常设计为右键点击列表中的子项然后弹出菜单,如果我们不能分清是鼠标左键还是右键点击消息,就没办法进行开发,所以本文将区分列表中子项的鼠标左键和鼠标右键点击消息。

2 源码修改

2.1 修改Core/UIDefine.h

修改Core/UIDefine.h文件,在该文件中增加列表子项点击的右键消息,如下

#define DUI_MSGTYPE_ITEMRCLICK                (_T("itemrclick"))

我们将这个消息定义为DUI_MSGTYPE_ITEMRCLICK

2.2 修改UI/UIList.cpp

修改UI/UIList.cpp,原有的子项CListContainerElementUIDoEvent(TEventUI& event)函数源码如下,

void CListContainerElementUI::DoEvent(TEventUI& event)
{
    if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) {
        if( m_pOwner != NULL ) m_pOwner->DoEvent(event);
        else CContainerUI::DoEvent(event);
        return;
    }

    if( event.Type == UIEVENT_DBLCLICK )
    {
        if( IsEnabled() ) {
            Activate();
            Invalidate();
        }
        return;
    }
    if( event.Type == UIEVENT_KEYDOWN )
    {
        if (IsKeyboardEnabled() && IsEnabled()) {
            if( event.chKey == VK_RETURN ) {
                Activate();
                Invalidate();
                return;
            }
        }
    }
    if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_RBUTTONDOWN )
    {
        if( IsEnabled() ) {
            m_pManager->SendNotify(this, DUI_MSGTYPE_ITEMCLICK);
            Select();
            Invalidate();
        }
        return;
    }
    if( event.Type == UIEVENT_BUTTONUP ) 
    {
        return;
    }
    if( event.Type == UIEVENT_MOUSEMOVE )
    {
        return;
    }
    if( event.Type == UIEVENT_MOUSEENTER )
    {
        if( ::PtInRect(&m_rcItem, event.ptMouse ) ) {
            if( IsEnabled() ) {
                if( (m_uButtonState & UISTATE_HOT) == 0  ) {
                    m_uButtonState |= UISTATE_HOT;
                    Invalidate();
                }
            }
        }
    }
    if( event.Type == UIEVENT_MOUSELEAVE )
    {
        if( !::PtInRect(&m_rcItem, event.ptMouse ) ) {
            if( IsEnabled() ) {
                if( (m_uButtonState & UISTATE_HOT) != 0  ) {
                    m_uButtonState &= ~UISTATE_HOT;
                    Invalidate();
                }
            }
            if (m_pManager) m_pManager->RemoveMouseLeaveNeeded(this);
        }
        else {
            if (m_pManager) m_pManager->AddMouseLeaveNeeded(this);
            return;
        }
    }

    // An important twist: The list-item will send the event not to its immediate
    // parent but to the "attached" list. A list may actually embed several components
    // in its path to the item, but key-presses etc. needs to go to the actual list.
    if( m_pOwner != NULL ) m_pOwner->DoEvent(event); else CControlUI::DoEvent(event);
}

我们将鼠标左键消息与鼠标邮件消息进行分离,修改后的DoEvent(TEventUI& event)函数如下:

void CListContainerElementUI::DoEvent(TEventUI& event)
{
    if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) {
        if( m_pOwner != NULL ) m_pOwner->DoEvent(event);
        else CContainerUI::DoEvent(event);
        return;
    }

    if( event.Type == UIEVENT_DBLCLICK )
    {
        if( IsEnabled() ) {
            Activate();
            Invalidate();
        }
        return;
    }
    if( event.Type == UIEVENT_KEYDOWN )
    {
        if (IsKeyboardEnabled() && IsEnabled()) {
            if( event.chKey == VK_RETURN ) {
                Activate();
                Invalidate();
                return;
            }
        }
    }
    if( event.Type == UIEVENT_BUTTONDOWN)
    {
        if( IsEnabled() ) {
            m_pManager->SendNotify(this, DUI_MSGTYPE_ITEMCLICK);
            Select();
            Invalidate();
        }
        return;
    }
    if (event.Type == UIEVENT_RBUTTONDOWN)
    {
        if (IsEnabled()) {
            m_pManager->SendNotify(this, DUI_MSGTYPE_ITEMRCLICK);
            Select();
            Invalidate();
        }
        return;
    }
    if( event.Type == UIEVENT_BUTTONUP ) 
    {
        return;
    }
    if( event.Type == UIEVENT_MOUSEMOVE )
    {
        return;
    }
    if( event.Type == UIEVENT_MOUSEENTER )
    {
        if( ::PtInRect(&m_rcItem, event.ptMouse ) ) {
            if( IsEnabled() ) {
                if( (m_uButtonState & UISTATE_HOT) == 0  ) {
                    m_uButtonState |= UISTATE_HOT;
                    Invalidate();
                }
            }
        }
    }
    if( event.Type == UIEVENT_MOUSELEAVE )
    {
        if( !::PtInRect(&m_rcItem, event.ptMouse ) ) {
            if( IsEnabled() ) {
                if( (m_uButtonState & UISTATE_HOT) != 0  ) {
                    m_uButtonState &= ~UISTATE_HOT;
                    Invalidate();
                }
            }
            if (m_pManager) m_pManager->RemoveMouseLeaveNeeded(this);
        }
        else {
            if (m_pManager) m_pManager->AddMouseLeaveNeeded(this);
            return;
        }
    }

    // An important twist: The list-item will send the event not to its immediate
    // parent but to the "attached" list. A list may actually embed several components
    // in its path to the item, but key-presses etc. needs to go to the actual list.
    if( m_pOwner != NULL ) m_pOwner->DoEvent(event); else CControlUI::DoEvent(event);
}

通过上述代码我们为Duilib扩展了列表子项的右键点击消息响应函数。

2.3 如何使用

在List控件的主窗口的Notify(TNotifyUI& msg)函数中增加对消息的响应,

else if (_tcsicmp(msg.sType, _T("itemrclick")) == 0)
{
    ProcessItemrclickMessage(msg);
}

然后在ProcessItemrclickMessage函数中判断msg.pSender的父控件是哪个List控件,然后弹出相应的菜单即可。

参考链接