菜单是一个特殊的窗口,特殊在 在指定鼠标位置显示,焦点消失时,自动销毁,无任务栏图标等
我们看看,duilib是如何实现这些功能的
enum MenuAlignment
{
eMenuAlignment_Left = 1 << 1,
eMenuAlignment_Top = 1 << 2,
eMenuAlignment_Right = 1 << 3,
eMenuAlignment_Bottom = 1 << 4,
};
菜单类
extern const TCHAR* const kMenuElementUIInterfaceName;// = _T("MenuElement);
class CMenuElementUI;
class CMenuWnd : public ui::WindowImplBase
{
public:
virtual Control* CreateControl(const std::wstring& pstrClass) override;
enum PopupPosType
{
RIGHT_BOTTOM,
RIGHT_TOP
};
CMenuWnd(HWND hParent = NULL);
void Init(STRINGorID xml, LPCTSTR pSkinType, POINT point, PopupPosType popupPosType = RIGHT_BOTTOM);
std::wstring GetWindowClassName() const;
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
virtual std::wstring GetSkinFolder() override {
return L"menu";
}
virtual std::wstring GetSkinFile() override {
return m_xml.m_lpstr;
}
void Show();
public:
HWND m_hParent;
POINT m_BasedPoint;
PopupPosType m_popupPosType;
STRINGorID m_xml;
std::wstring m_sType;
};
其中,在Init中,创建菜单窗口
void CMenuWnd::Init(STRINGorID xml, LPCTSTR pSkinType, POINT point, PopupPosType popupPosType)
{
m_BasedPoint = point;
m_popupPosType = popupPosType;
if (pSkinType != NULL)
m_sType = pSkinType;
m_xml = xml;
Create(m_hParent, L"菜单", 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, SW_SHOW);
::SendMessage(hWndParent, WM_NCACTIVATE, TRUE, 0L);
}
在HandleMessage中处理焦点消失时 销毁窗口功能
LRESULT CMenuWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if( uMsg == WM_KILLFOCUS )
{
Close();
return 0;
}
else if( uMsg == WM_KEYDOWN)
{
if( wParam == VK_ESCAPE)
{
Close();
}
}
return __super::HandleMessage(uMsg, wParam, lParam);
}
在show中处理菜单显示位置
void CMenuWnd::Show()
{
MONITORINFO oMonitor = {};
oMonitor.cbSize = sizeof(oMonitor);
if (m_hParent!=NULL)
{
::GetMonitorInfo(::MonitorFromWindow(m_hParent, MONITOR_DEFAULTTOPRIMARY), &oMonitor);
}else
::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
{
ASSERT(FALSE);
}
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, SWP_SHOWWINDOW, HWND_TOPMOST, false);
SetForegroundWindow(m_hWnd);
}
在CreateControl中处理菜单元素
Control* CMenuWnd::CreateControl(const std::wstring& pstrClass)
{
if (pstrClass == kMenuElementUIInterfaceName)
{
return new CMenuElementUI();
}
return NULL;
}
下面再看看菜单元素类
class ListContainerElement;
class CMenuElementUI : public ListContainerElement
{
friend CMenuWnd;
public:
CMenuElementUI();
~CMenuElementUI();
virtual bool ButtonUp(EventArgs& msg) override;
};
//其中,ListContainerElement继承自盒子选项
class UILIB_API ListContainerElement : public OptionTemplate
{
public:
ListContainerElement();
int GetIndex() const;
void SetIndex(int iIndex);
IListOwner* GetOwner();
void SetOwner(IListOwner* pOwner);
void SetVisible(bool bVisible = true);
void Selected(bool bSelect, bool trigger) override;
void InvokeDoubleClickEvent();
virtual void HandleMessage(EventArgs& event) override;
void AttachDoubleClick(const EventCallback& callback)
{
OnEvent[kEventMouseDoubleClick] += callback;
}
void AttachReturn(const EventCallback& callback)
{
OnEvent[kEventReturn] += callback;
}
protected:
int m_iIndex;
IListOwner* m_pOwner;
};
//再往上翻
template
class UILIB_API OptionTemplate : public CheckBoxTemplate
{
public:
OptionTemplate();
~OptionTemplate();
virtual void SetWindow(Window* pManager, Box* pParent, bool bInit = true) override;
virtual void SetAttribute(const std::wstring& pstrName, const std::wstring& pstrValue) override;
void SetGroup(const std::wstring& pStrGroupName);
std::wstring GetGroup() const;
virtual void Selected(bool bSelected, bool bTriggerEvent = false) override;
virtual void Activate() override;
protected:
std::wstring m_sGroupName;
};
#include "OptionImpl.h"
typedef OptionTemplate Option;
typedef OptionTemplate OptionBox;
//继续翻
template
class UILIB_API CheckBoxTemplate : public ButtonTemplate
{
public:
CheckBoxTemplate();
virtual void Activate() override;
std::wstring GetSelectedStateImage(ControlStateType stateType);
void SetSelectedStateImage(ControlStateType stateType, const std::wstring& pStrImage);
void SetSelectedTextColor(const std::wstring& dwTextColor);
std::wstring GetSelectedTextColor();
void SetSelectedStateColor(ControlStateType stateType, const std::wstring& stateColor);
std::wstring GetSelectStateColor(ControlStateType stateType);
std::wstring GetSelectedForeStateImage(ControlStateType stateType);
void SetSelectedForeStateImage(ControlStateType stateType, const std::wstring& pStrImage);
bool IsSelected() const;
virtual void Selected(bool bSelected, bool bTriggerEvent = false);
virtual void SetAttribute(const std::wstring& pstrName, const std::wstring& pstrValue) override;
virtual void PaintStatusColor(HDC hDC) override;
virtual void PaintStatusImage(HDC hDC) override;
virtual void PaintText(HDC hDC) override;
virtual Image* GetEstimateImage() override;
void AttachSelect(const EventCallback& callback)
{
OnEvent[kEventSelect] += callback;
}
void AttachUnSelect(const EventCallback& callback)
{
OnEvent[kEventUnSelect] += callback;
}
protected:
bool m_bSelected;
std::wstring m_dwSelectedTextColor;
StateColorMap m_selectedColorMap;
};
其中,有个AttachSelect, 绑定事件, 就是选中菜单某项时,要执行的事件,后续我们使用这个来绑定菜单元素选中时,要执行的函数
菜单元素类源码:
// MenuElementUI
const TCHAR* const kMenuElementUIInterfaceName = _T("MenuElement");
CMenuElementUI::CMenuElementUI()
{
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()) {
m_pWindow->Close();
}
return ret;
}
菜单的具体使用
1 )菜单位置
//点击按钮,跳出菜单
bool MyClassForm::UserMenuBtnClick(ui::EventArgs* param){
RECT rect = param->pSender->GetPos();
CPoint pt;
pt.x = rect.left - 65;
pt.y = rect.bottom + 10;
ClientToScreen(m_hWnd, &pt);
PopupUserMenu(pt);
return true;
}
if (uMsg==WM_RBUTTONDOWN)
{
int xPos = LOWORD(lParam);
INT yPos = HIWORD(lParam);
CPoint pt;
pt.x = xPos;
pt.y = yPos;
ClientToScreen(m_hWnd, &pt);
PopupUserMenu(pt);
return 1;
}
2)创建并显示菜单
PopupUserMenu(POINT point){
CMenuWnd* pMenu = new CMenuWnd(m_hWnd);
STRINGorID xml(L"myClass_user_menu.xml");
pMenu->Init(xml, _T("xml"), point);
CMenuElementUI *pUserExit = (CMenuElementUI*)pMenu->FindControl(L"userExit");
pUserExit->AttachSelect(nbase::Bind(&MyClassForm::MenuItemClick_UserExit, this, std::placeholders::_1));
CMenuElementUI *pFreshClass = (CMenuElementUI*)pMenu->FindControl(L"freshClass");
pFreshClass->AttachSelect(nbase::Bind(&MyClassForm::MenuItemClick_FreshClass, this, std::placeholders::_1));
CMenuElementUI *pAbout = (CMenuElementUI*)pMenu->FindControl(L"xnwAbout");
pAbout->AttachSelect(nbase::Bind(&MyClassForm::MenuItemClick_About, this, std::placeholders::_1));
//CMenuElementUI *pSendLog = (CMenuElementUI*)pMenu->FindControl(L"sendLog");
//pSendLog->AttachSelect(nbase::Bind(&MyClassForm::MenuItemClick_SendLog, this, std::placeholders::_1));
//
CMenuElementUI *pClassSet = (CMenuElementUI*)pMenu->FindControl(L"ClassSet");
pClassSet->AttachSelect(nbase::Bind(&MyClassForm::MenuItemClick_ClassSet, this, std::placeholders::_1));
pMenu->Show();
pMenu->SetFocus(NULL);
}
3)菜单XML布局
//控制选中 菜单项时 对应的颜色状态