Duilib的多级菜单实现(网易云信版本)
阅读原文时间:2023年07月15日阅读:1

完整代码见:https://github.com/netease-im/NIM_Duilib_Framework/tree/master/ui_components/menu

核心代码:

ui_menu.h

#ifndef __UIMENU_H__
#define __UIMENU_H__

#pragma once

namespace nim_comp {

using namespace ui;
enum MenuAlignment
{
eMenuAlignment_Left = << ,
eMenuAlignment_Top = << ,
eMenuAlignment_Right = << ,
eMenuAlignment_Bottom = << ,
eMenuAlignment_Intelligent = << //智能的防止被遮蔽
};

enum MenuCloseType
{
eMenuCloseThis, //适用于关闭当前级别的菜单窗口,如鼠标移入时
eMenuCloseAll //关闭所有菜单窗口,如失去焦点时
};

//增加关闭事件的传递。
/*
点击某一菜单,获取该菜单窗口句柄,通知该菜单窗口可以关闭子菜单项了。
即某子菜单项目的父窗口等于该窗口,该子菜单关闭。
由于菜单的父子关系,会自动关闭其所有子孙菜单窗口
这里的事件传递设计拷贝原生Duilib的MenuDemo,不过Redrain的Menu功能更好,支持菜单复选,这里暂未实现
*/
#include "observer_impl_base.hpp" //copy from menuDemo
struct ContextMenuParam
{
MenuCloseType wParam;
HWND hWnd;
};

typedef class ObserverImpl ContextMenuObserver;
typedef class ReceiverImpl ContextMenuReceiver;

/////////////////////////////////////////////////////////////////////////////////////
//

extern const TCHAR* const kMenuElementUIInterfaceName;// = _T("MenuElement);
class CMenuElementUI;
class CMenuWnd : public ui::WindowImplBase, public ContextMenuReceiver
{
public:
enum PopupPosType //鼠标点击的point属于菜单的哪个位置 1.-----.2 1左上 2右上
{ // | |
//这里假定用户是喜欢智能的 3.-----.4 3左下 4右下
RIGHT_BOTTOM = eMenuAlignment_Right | eMenuAlignment_Bottom | eMenuAlignment_Intelligent,
RIGHT_TOP = eMenuAlignment_Right | eMenuAlignment_Top | eMenuAlignment_Intelligent,
LEFT_BOTTOM = eMenuAlignment_Left | eMenuAlignment_Bottom | eMenuAlignment_Intelligent,
LEFT_TOP = eMenuAlignment_Intelligent | eMenuAlignment_Top | eMenuAlignment_Intelligent,
//这里是normal,非智能的
RIGHT_BOTTOM_N = eMenuAlignment_Right | eMenuAlignment_Bottom,
RIGHT_TOP_N = eMenuAlignment_Right | eMenuAlignment_Top,
LEFT_BOTTOM_N = eMenuAlignment_Left | eMenuAlignment_Bottom,
LEFT_TOP_N = eMenuAlignment_Intelligent | eMenuAlignment_Top
};
CMenuWnd(HWND hParent = NULL);
void Init(STRINGorID xml, LPCTSTR pSkinType, POINT point, PopupPosType popupPosType = LEFT_TOP, bool no_focus = false, CMenuElementUI* pOwner = NULL);
void Show();
// 重新调整菜单的大小
void ResizeMenu();
// 重新调整子菜单的大小
void ResizeSubMenu();

static ContextMenuObserver& GetMenuObserver()  
{  
    static ContextMenuObserver s\_context\_menu\_observer;  
    return s\_context\_menu\_observer;  
}  
BOOL Receive(ContextMenuParam param) override;

virtual Control\* CreateControl(const std::wstring& pstrClass) override;  
virtual std::wstring GetSkinFolder() override {  
    return L"menu";  
}  
virtual std::wstring GetSkinFile() override {  
    return m\_xml.m\_lpstr;  
}  
std::wstring GetWindowClassName() const override;

public:
HWND m_hParent;
POINT m_BasedPoint;
PopupPosType m_popupPosType;
STRINGorID m_xml;
bool no_focus_;
CMenuElementUI* m_pOwner;
ListBox* m_pLayout;
private:
virtual void InitWindow() override;
void CMenuWnd::OnFinalMessage(HWND hWnd) override;
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);

};

class ListContainerElement;
class CMenuElementUI : public ui::ListContainerElement
{
friend CMenuWnd;
public:
CMenuElementUI();
~CMenuElementUI();

virtual bool ButtonUp(EventArgs& msg) override;  
virtual bool MouseEnter(EventArgs& msg) override;

virtual void PaintChild(IRenderContext\* pRender, const UiRect& rcPaint) override;

bool CheckSubMenuItem();  

private:

void CreateMenuWnd();  
CMenuWnd\*    m\_pSubWindow;  

};

} // namespace nim_comp

#endif // __UIMENU_H__

ui_menu.cpp

#include "stdafx.h"
#include "ui_menu.h"

namespace nim_comp {

/////////////////////////////////////////////////////////////////////////////////////
//

Control* CMenuWnd::CreateControl(const std::wstring& pstrClass)
{
if (pstrClass == kMenuElementUIInterfaceName)
{
return new CMenuElementUI();
}
return NULL;
}

BOOL CMenuWnd::Receive(ContextMenuParam param)
{
switch (param.wParam)
{
case eMenuCloseAll:
Close();
break;
case eMenuCloseThis:
{
HWND hParent = GetParent(m_hWnd);
while (hParent != NULL)
{
if (hParent == param.hWnd)
{
Close();
break;
}
hParent = GetParent(hParent);
}
}
break;
default:
break;
}

return TRUE;  

}

CMenuWnd::CMenuWnd(HWND hParent) :
m_hParent(hParent),
m_xml(_T("")),
no_focus_(false),
m_pOwner(nullptr),
m_pLayout(nullptr)
{
}

void CMenuWnd::Init(STRINGorID xml, LPCTSTR pSkinType, POINT point, PopupPosType popupPosType, bool no_focus, CMenuElementUI* pOwner)
{
m_BasedPoint = point;
m_popupPosType = popupPosType;

m\_xml = xml;  
no\_focus\_ = no\_focus;  
m\_pOwner = pOwner;

CMenuWnd::GetMenuObserver().AddReceiver(this);

Create(m\_hParent, L"NIM\_DUILIB\_MENU\_WINDOW", WS\_POPUP, WS\_EX\_TOOLWINDOW | WS\_EX\_TOPMOST, true, UiRect());  
// HACK: Don't deselect the parent's caption  
HWND hWndParent = m\_hWnd;  
while (::GetParent(hWndParent) != NULL) hWndParent = ::GetParent(hWndParent);  
::ShowWindow(m\_hWnd, no\_focus ? SW\_SHOWNOACTIVATE : SW\_SHOW);  
if (m\_pOwner)  
{  
    ResizeSubMenu();  
}  
else  
{  
    ResizeMenu();  
}  
::SendMessage(hWndParent, WM\_NCACTIVATE, TRUE, 0L);  

}

void CMenuWnd::OnFinalMessage(HWND hWnd)
{
Window::OnFinalMessage(hWnd);
RemoveObserver();
if (m_pOwner != NULL) {
m_pLayout->SelectItem(-);
for (int i = ; i < m_pLayout->GetCount(); i++) {
CMenuElementUI* pItem = static_cast(m_pLayout->GetItemAt(i)); //这里确定是CMenuElementUI*,static_cast效率高
if (pItem)
{
pItem->SetOwner(dynamic_cast(m_pOwner->GetParent()));//这里的父控件可能仍然是menuitem,那么置空即可
pItem->SetWindow(m_pOwner->GetWindow(), m_pOwner, false); //更改item的归属
// pItem->SetVisible(false);
pItem->SetInternVisible(false);
}
}
m_pLayout->RemoveAll();
m_pOwner->m_pSubWindow = NULL;
//m_pOwner->m_uButtonState &= ~UISTATE_PUSHED; 这里可能需要替换,暂时注释
m_pOwner->Invalidate();
}
ReapObjects(GetRoot());
delete this;
}

std::wstring CMenuWnd::GetWindowClassName() const
{
return _T("MenuWnd");
}

LRESULT CMenuWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_KILLFOCUS:
{
HWND hFocusWnd = (HWND)wParam;

    BOOL bInMenuWindowList = FALSE;  
    ContextMenuParam param;  
    param.hWnd = GetHWND();

    ContextMenuObserver::Iterator<BOOL, ContextMenuParam> iterator(GetMenuObserver());  
    ReceiverImplBase<BOOL, ContextMenuParam>\* pReceiver = iterator.next();  
    while (pReceiver != NULL) {  
        CMenuWnd\* pContextMenu = dynamic\_cast<CMenuWnd\*>(pReceiver);  
        if (pContextMenu != NULL && pContextMenu->GetHWND() == hFocusWnd) {  
            bInMenuWindowList = TRUE;  
            break;  
        }  
        pReceiver = iterator.next();  
    }

    if (!bInMenuWindowList) {  
        param.wParam = eMenuCloseAll;  
        GetMenuObserver().RBroadcast(param);

        return ;  
    }  
}  
    break;  
case WM\_KEYDOWN:  
    if (wParam == VK\_ESCAPE || wParam == VK\_LEFT)  
        Close();  
    else if (wParam == VK\_RIGHT)  
    {  
        if (m\_pLayout)  
        {  
            int index = m\_pLayout->GetCurSel();  
            CMenuElementUI\* pItem = dynamic\_cast<CMenuElementUI\*>(m\_pLayout->GetItemAt(index));  
            if (pItem)  
            {  
                pItem->CheckSubMenuItem();  
            }  
        }  
    }  
    else if (wParam == VK\_RETURN || wParam == VK\_SPACE)  
    {  
        if (m\_pLayout)  
        {  
            int index = m\_pLayout->GetCurSel();  
            CMenuElementUI\* pItem = dynamic\_cast<CMenuElementUI\*>(m\_pLayout->GetItemAt(index));  
            if (pItem)  
            {  
                if (!pItem->CheckSubMenuItem())  
                {  
                    ContextMenuParam param;  
                    param.hWnd = m\_hWnd;  
                    param.wParam = eMenuCloseAll;  
                    CMenuWnd::GetMenuObserver().RBroadcast(param);  
                }  
            }  
        }  
    }  
    break;  
case WM\_RBUTTONDOWN:  
case WM\_CONTEXTMENU:  
case WM\_RBUTTONUP:  
case WM\_RBUTTONDBLCLK:  
    return 0L;

default:  
    break;  
}

return \_\_super::HandleMessage(uMsg, wParam, lParam);  

}

void CMenuWnd::ResizeMenu()
{
Control* pRoot = GetRoot();
MONITORINFO oMonitor = {};
oMonitor.cbSize = sizeof(oMonitor);
//点击在哪里,以哪里的屏幕为主
::GetMonitorInfo(::MonitorFromPoint(m_BasedPoint, MONITOR_DEFAULTTOPRIMARY), &oMonitor);
UiRect rcWork = oMonitor.rcWork;

CSize szAvailable = { rcWork.right - rcWork.left, rcWork.bottom - rcWork.top };  
szAvailable = pRoot->EstimateSize(szAvailable);   //这里带上了阴影窗口  
SetInitSize(szAvailable.cx, szAvailable.cy);  
UiRect rcCorner = GetShadowCorner();  
CSize szInit=szAvailable;  
szInit.cx -= rcCorner.left + rcCorner.right;  
szInit.cy -= rcCorner.top + rcCorner.bottom; //这里去掉阴影窗口,即用户的视觉有效面积 szInit<=szAvailable

CPoint point = m\_BasedPoint;  //这里有个bug,由于坐标点与包含在窗口内,会直接出发mouseenter导致出来子菜单,偏移1个像素  
if (m\_popupPosType & eMenuAlignment\_Right)  
{  
    point.x += -szAvailable.cx + rcCorner.right + rcCorner.left;  
    point.x -= ;  
}  
else if (m\_popupPosType & eMenuAlignment\_Left)  
{  
    point.x += ;  
}  
if (m\_popupPosType & eMenuAlignment\_Bottom)  
{  
    point.y += -szAvailable.cy + rcCorner.bottom + rcCorner.top;  
    point.y += ;  
}  
else if (m\_popupPosType & eMenuAlignment\_Top)  
{  
    point.y += ;  
}  
if (m\_popupPosType&eMenuAlignment\_Intelligent)  
{  
    if (point.x < rcWork.left)  
    {  
        point.x = rcWork.left;  
    }  
    else if (point.x + szInit.cx> rcWork.right)  
    {  
        point.x = rcWork.right - szInit.cx;  
    }  
    if (point.y < rcWork.top)  
    {  
        point.y = rcWork.top ;  
    }  
    else if (point.y + szInit.cy > rcWork.bottom)  
    {  
        point.y = rcWork.bottom - szInit.cy;  
    }  
}  
if (!no\_focus\_)  
{  
    SetForegroundWindow(m\_hWnd);  
    SetFocus(m\_pLayout);  
}  
SetWindowPos(m\_hWnd, HWND\_TOPMOST, point.x - rcCorner.left, point.y-rcCorner.top,  
    szAvailable.cx, szAvailable.cy,  
    SWP\_SHOWWINDOW | (no\_focus\_ ? SWP\_NOACTIVATE : ));  

}

void CMenuWnd::ResizeSubMenu()
{
// Position the popup window in absolute space
RECT rcOwner = m_pOwner->GetPos();
RECT rc = rcOwner;

int cxFixed = ;  
int cyFixed = ;

MONITORINFO oMonitor = {};  
oMonitor.cbSize = sizeof(oMonitor);  
::GetMonitorInfo(::MonitorFromPoint(m\_BasedPoint, MONITOR\_DEFAULTTOPRIMARY), &oMonitor);  
UiRect rcWork = oMonitor.rcWork;  
CSize szAvailable = { rcWork.right - rcWork.left, rcWork.bottom - rcWork.top };

for (int it = ; it < m\_pLayout->GetCount(); it++) {  
    //取子菜单项中的最大值作为菜单项  
    CMenuElementUI\* pItem = dynamic\_cast<CMenuElementUI\*>(m\_pLayout->GetItemAt(it));  
    if (pItem)  
    {  
        SIZE sz = pItem->EstimateSize(szAvailable);  
        cyFixed += sz.cy;

        if (cxFixed < sz.cx)  
            cxFixed = sz.cx;  
    }  
}  
UiRect rcCorner = GetShadowCorner();  
RECT rcWindow;  
GetWindowRect(m\_pOwner->GetWindow()->GetHWND(), &rcWindow);  
//去阴影  
{  
    rcWindow.left += rcCorner.left;  
    rcWindow.right -= rcCorner.right;  
    rcWindow.top += rcCorner.top;  
    rcWindow.bottom -=  rcCorner.bottom;  
}

::MapWindowRect(m\_pOwner->GetWindow()->GetHWND(), HWND\_DESKTOP, &rc);

rc.left = rcWindow.right;  
rc.right = rc.left + cxFixed;  
rc.bottom = rc.top + cyFixed;

bool bReachBottom = false;  
bool bReachRight = false;  
LONG chRightAlgin = ;  
LONG chBottomAlgin = ;

RECT rcPreWindow = {  };  
ContextMenuObserver::Iterator<BOOL, ContextMenuParam> iterator(GetMenuObserver());  
ReceiverImplBase<BOOL, ContextMenuParam>\* pReceiver = iterator.next();  
while (pReceiver != NULL) {  
    CMenuWnd\* pContextMenu = dynamic\_cast<CMenuWnd\*>(pReceiver);  
    if (pContextMenu != NULL) {  
        GetWindowRect(pContextMenu->GetHWND(), &rcPreWindow);  //需要减掉阴影

        bReachRight = (rcPreWindow.left + rcCorner.left) >= rcWindow.right;  
        bReachBottom = (rcPreWindow.top + rcCorner.top) >= rcWindow.bottom;  
        if (pContextMenu->GetHWND() == m\_pOwner->GetWindow()->GetHWND()  
            || bReachBottom || bReachRight)  
            break;  
    }  
    pReceiver = iterator.next();  
}  
if (bReachBottom)  
{  
    rc.bottom = rcWindow.top;  
    rc.top = rc.bottom - cyFixed;  
}

if (bReachRight)  
{  
    rc.right = rcWindow.left;  
    rc.left = rc.right - cxFixed;  
}

if (rc.bottom > rcWork.bottom)  
{  
    rc.bottom = rc.top;  
    rc.top = rc.bottom - cyFixed;  
}

if (rc.right > rcWork.right)  
{  
    rc.right = rcWindow.left;  
    rc.left = rc.right - cxFixed;  
}

if (rc.top < rcWork.top)  
{  
    rc.top = rcOwner.top;  
    rc.bottom = rc.top + cyFixed;  
}

if (rc.left < rcWork.left)  
{  
    rc.left = rcWindow.right;  
    rc.right = rc.left + cxFixed;  
}

SetWindowPos(m\_hWnd, HWND\_TOPMOST, rc.left-rcCorner.left, rc.top-rcCorner.top,  
    rc.right - rc.left, rc.bottom - rc.top,  
    SWP\_SHOWWINDOW);

SetForegroundWindow(m\_hWnd);  
SetFocus(m\_pLayout);  

}

void CMenuWnd::Show()
{
MONITORINFO oMonitor = {};
oMonitor.cbSize = sizeof(oMonitor);
::GetMonitorInfo(::MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTOPRIMARY), &oMonitor);
UiRect rcWork = oMonitor.rcWork;
UiRect monitor_rect = oMonitor.rcMonitor;
ui::CSize szInit = { rcWork.right - rcWork.left, rcWork.bottom - rcWork.top };
szInit = GetRoot()->EstimateSize(szInit);
szInit.cx -= GetShadowCorner().left + GetShadowCorner().right;
szInit.cy -= GetShadowCorner().top + GetShadowCorner().bottom;
if (m_popupPosType == RIGHT_BOTTOM)
{
if (m_BasedPoint.y + szInit.cy > monitor_rect.bottom)
{
m_BasedPoint.y -= szInit.cy;
}
}
else if (m_popupPosType == RIGHT_TOP)
{
if (m_BasedPoint.y - szInit.cy >= monitor_rect.top)
{
m_BasedPoint.y -= szInit.cy;
}
}
else
{
//兼容老版本
return;
}
UiRect rc;
rc.left = m_BasedPoint.x;
rc.top = m_BasedPoint.y;
if (rc.top < monitor_rect.top)
{
rc.top = monitor_rect.top;
}

//判断是否超出屏幕  
if (rc.left > monitor\_rect.right - szInit.cx)  
{  
    rc.left = monitor\_rect.right - szInit.cx;  
}  
if (rc.left < monitor\_rect.left)  
{  
    rc.left = monitor\_rect.left;  
}  
rc.right = rc.left + szInit.cx;  
rc.bottom = rc.top + szInit.cy;

SetPos(rc, false, SWP\_SHOWWINDOW | (no\_focus\_ ? SWP\_NOACTIVATE : ), HWND\_TOPMOST, false);  
if (!no\_focus\_)  
    SetForegroundWindow(m\_hWnd);  

}

void CMenuWnd::InitWindow()
{
if (m_pOwner)
{
m_pLayout = dynamic_cast(FindControl(L"submenu"));
ASSERT(m_pLayout);
m_pLayout->SetAutoDestroy(false);

    for (int i = ; i < m\_pOwner->GetCount(); i++) {  
        CMenuElementUI\* subMenuItem = dynamic\_cast<CMenuElementUI\*>(m\_pOwner->GetItemAt(i));  
        if (subMenuItem && subMenuItem->IsVisible())  
        {  
            //此时子菜单item属于2个子菜单,注意生命周期的维护,子菜单窗口退出不能销毁控件,需要归还原控件,  
            //此时子菜单item的父控件是准的,但父控件可能不是Vlist,SetOwner的入参是Vlist,这时owner置空  
            //见OnFinalMessage  
            m\_pLayout->Add(subMenuItem); //内部会调用subMenuItem->SetOwner(m\_pLayout); 会调用SetWindows,改变了归属窗口、父控件。  
        }  
    }  
}  
else  
{  
    m\_pLayout = dynamic\_cast<ListBox\*>(m\_pRoot);  
    if (m\_pLayout == NULL)  
    {  
        //允许外面套层阴影  
        if (m\_pRoot->GetCount()>)  
        {  
            m\_pLayout = dynamic\_cast<ListBox\*>(m\_pRoot->GetItemAt());  
        }  
    }  
    ASSERT(m\_pLayout);  
}  

}

// MenuElementUI
const TCHAR* const kMenuElementUIInterfaceName = _T("MenuElement");

CMenuElementUI::CMenuElementUI() :
m_pSubWindow(nullptr)
{
m_bMouseChildEnabled = false;
}

CMenuElementUI::~CMenuElementUI()
{}

bool CMenuElementUI::ButtonUp(EventArgs& msg)
{
std::weak_ptr weakFlag = m_pWindow->GetWeakFlag();
bool ret = __super::ButtonUp(msg);
if (ret && !weakFlag.expired()) {
//这里处理下如果有子菜单则显示子菜单
if (!CheckSubMenuItem())
{
ContextMenuParam param;
param.hWnd = GetWindow()->GetHWND();
param.wParam = eMenuCloseAll;
CMenuWnd::GetMenuObserver().RBroadcast(param);
}
}

return ret;  

}

bool CMenuElementUI::MouseEnter(EventArgs& msg)
{
std::weak_ptr weakFlag = m_pWindow->GetWeakFlag();
bool ret = __super::MouseEnter(msg);
if (ret && !weakFlag.expired()) {
//这里处理下如果有子菜单则显示子菜单
if (!CheckSubMenuItem())
{
ContextMenuParam param;
param.hWnd = GetWindow()->GetHWND();
param.wParam = eMenuCloseThis;
CMenuWnd::GetMenuObserver().RBroadcast(param);
//m_pOwner->SelectItem(GetIndex(), true); 有些老版本attachselect会触发
//这里得把之前选中的置为未选中
m_pOwner->SelectItem(-, false);
}
}

return ret;  

}

void CMenuElementUI::PaintChild(IRenderContext* pRender, const UiRect& rcPaint)
{
UiRect rcTemp;
if (!::IntersectRect(&rcTemp, &rcPaint, &m_rcItem)) return;

for (auto it = m\_items.begin(); it != m\_items.end(); it++) {  
    //尝试转CMenuElementUI  
    CMenuElementUI\* subMenuItem = dynamic\_cast<CMenuElementUI\*>(\*it);  
    if (subMenuItem)  
    {  
        continue;  
    }  
    Control\* pControl = \*it;  
    if (!pControl->IsVisible()) continue;  
    pControl->AlphaPaint(pRender, rcPaint);  
}  

}

bool CMenuElementUI::CheckSubMenuItem()
{
bool hasSubMenu = false;
for (int i = ; i < GetCount(); ++i) { CMenuElementUI* subMenuItem = dynamic_cast(GetItemAt(i));
if (subMenuItem )
{
//subMenuItem->SetVisible(true);
subMenuItem->SetInternVisible(true);
hasSubMenu = true;
}
}
if (hasSubMenu)
{
m_pOwner->SelectItem(GetIndex(), true);
CreateMenuWnd();
}
return hasSubMenu;
}

void CMenuElementUI::CreateMenuWnd()
{
if (m_pSubWindow) return;
m_pSubWindow = new CMenuWnd(GetWindow()->GetHWND());

ContextMenuParam param;  
param.hWnd =GetWindow()->GetHWND();  
param.wParam = eMenuCloseThis;  
CMenuWnd::GetMenuObserver().RBroadcast(param);

m\_pSubWindow->Init(\_T("submenu.xml"), \_T(""), CPoint(), CMenuWnd::RIGHT\_BOTTOM, false, this);  

}

} // namespace ui

observer_impl_base.hpp

#ifndef OBSERVER_IMPL_BASE_HPP
#define OBSERVER_IMPL_BASE_HPP

#include

template
class ReceiverImplBase;

template
class ObserverImplBase
{
public:
virtual void AddReceiver(ReceiverImplBase* receiver) = ;
virtual void RemoveReceiver(ReceiverImplBase* receiver) = ;
virtual ReturnT Broadcast(ParamT param) = ;
virtual ReturnT RBroadcast(ParamT param) = ;
virtual ReturnT Notify(ParamT param) = ;
};

template
class ReceiverImplBase
{
public:
virtual void AddObserver(ObserverImplBase* observer) = ;
virtual void RemoveObserver() = ;
virtual ReturnT Receive(ParamT param) = ;
virtual ReturnT Respond(ParamT param, ObserverImplBase* observer) = ;
};

template
class ReceiverImpl;

template
class ObserverImpl : public ObserverImplBase
{
template
friend class Iterator;
public:
ObserverImpl()
{}

virtual ~ObserverImpl()    {}

virtual void AddReceiver(ReceiverImplBase<ReturnT, ParamT>\* receiver)  
{  
    if (receiver == NULL)  
        return;

    receivers\_.push\_back(receiver);  
    receiver->AddObserver(this);  
}

virtual void RemoveReceiver(ReceiverImplBase<ReturnT, ParamT>\* receiver)  
{  
    if (receiver == NULL)  
        return;

    ReceiversVector::iterator it = receivers\_.begin();  
    for (; it != receivers\_.end(); ++it)  
    {  
        if (\*it == receiver)  
        {  
            receivers\_.erase(it);  
            break;  
        }  
    }  
}

virtual ReturnT Broadcast(ParamT param)  
{  
    ReceiversVector::iterator it = receivers\_.begin();  
    for (; it != receivers\_.end(); ++it)  
    {  
        (\*it)->Receive(param);  
    }

    return ReturnT();  
}

virtual ReturnT RBroadcast(ParamT param)  
{  
    ReceiversVector::reverse\_iterator it = receivers\_.rbegin();  
    for (; it != receivers\_.rend(); ++it)  
    {  
        (\*it)->Receive(param);  
    }

    return ReturnT();  
}

virtual ReturnT Notify(ParamT param)  
{  
    ReceiversVector::iterator it = receivers\_.begin();  
    for (; it != receivers\_.end(); ++it)  
    {  
        (\*it)->Respond(param, this);  
    }

    return ReturnT();  
}

template <typename ReturnT, typename ParamT>  
class Iterator  
{  
    ObserverImpl<ReturnT, ParamT> & \_tbl;  
    DWORD index;  
    ReceiverImplBase<ReturnT, ParamT>\* ptr;  
public:  
    Iterator( ObserverImpl & table )  
        : \_tbl( table ), index(), ptr(NULL)  
    {}

    Iterator( const Iterator & v )  
        : \_tbl( v.\_tbl ), index(v.index), ptr(v.ptr)  
    {}

    ReceiverImplBase<ReturnT, ParamT>\* next()  
    {  
        if ( index >= \_tbl.receivers\_.size() )  
            return NULL;

        for ( ; index < \_tbl.receivers\_.size(); )  
        {  
            ptr = \_tbl.receivers\_\[ index++ \];  
            if ( ptr )  
                return ptr;  
        }  
        return NULL;  
    }  
};

protected:
typedef std::vector*> ReceiversVector;
ReceiversVector receivers_;
};

template
class ReceiverImpl : public ReceiverImplBase
{
public:
ReceiverImpl()
{}

virtual ~ReceiverImpl()    {}

virtual void AddObserver(ObserverImplBase<ReturnT, ParamT>\* observer)  
{  
    observers\_.push\_back(observer);  
}

virtual void RemoveObserver()  
{  
    ObserversVector::iterator it = observers\_.begin();  
    for (; it != observers\_.end(); ++it)  
    {  
        (\*it)->RemoveReceiver(this);  
    }  
}

virtual ReturnT Receive(ParamT param)  
{  
    return ReturnT();  
}

virtual ReturnT Respond(ParamT param, ObserverImplBase<ReturnT, ParamT>\* observer)  
{  
    return ReturnT();  
}

protected:
typedef std::vector*> ObserversVector;
ObserversVector observers_;
};

#endif // OBSERVER_IMPL_BASE_HPP

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章