diff --git a/SOUI/include/control/SMenuBar.h b/SOUI/include/control/SMenuBar.h index 27363cc434db8cd08e513d21f7730d35c9b3daf2..8a3fd8b61ba402c32e8377204122b2d7a33d7ca4 100644 --- a/SOUI/include/control/SMenuBar.h +++ b/SOUI/include/control/SMenuBar.h @@ -1,32 +1,42 @@ #pragma once -#include +#include namespace SOUI { class SMenuItem; - class SOUI_EXP SMenuBar : + class SMenuBar : public SWindow { SOUI_CLASS_NAME(SMenuBar, L"menubar") - friend class SMenuItem; + friend class SMenuItem; public: SMenuBar(); ~SMenuBar(); - BOOL Init(SHostWnd *pHostWnd); BOOL Insert(LPCTSTR pszTitle, LPCTSTR pszResName, int iPos = -1); + BOOL Insert(pugi::xml_node xmlNode, int iPos = -1); + SMenu* GetMenu(DWORD dwPos); + + int HitTest(CPoint pt); protected: + SMenuItem* GetMenuItem(DWORD dwPos); virtual BOOL CreateChildren(pugi::xml_node xmlNode); - virtual void UpdateChildrenPosition(); + + static LRESULT CALLBACK MenuSwitch(int code, WPARAM wParam, LPARAM lParam); SArray m_lstMenuItem; HWND m_hWnd; pugi::xml_document m_xmlStyle; BOOL m_bIsShow; - SMenuItem *m_pNowMenu; + SMenuItem* m_pNowMenu; + int m_iNowMenu; + CPoint m_ptMouse; + + static HHOOK m_hMsgHook; + static SMenuBar* m_pMenuBar; }; } \ No newline at end of file diff --git a/SOUI/include/event/Events.h b/SOUI/include/event/Events.h index ec2c89f9b68da691fa58763c3c3081160cead6f6..22ff9e4929d51f88b97ec3dafb1d14d4c956aa0d 100644 --- a/SOUI/include/event/Events.h +++ b/SOUI/include/event/Events.h @@ -139,6 +139,7 @@ namespace SOUI EVT_ANI_STOP, EVT_SELECTMENU = 22150, + EVT_POPMENU, EVT_EXTERNAL_BEGIN=10000000, @@ -1181,10 +1182,15 @@ namespace SOUI // enum{EventID=EVT_ANI_STOP}; //}; - class SMenuEx; + class SMenu; SEVENT_BEGIN_EX(EventSelectMenu, EVT_SELECTMENU, on_select_menu, SOUI_EXP) UINT m_id; - SMenuEx *m_pMenu; + SMenu *m_pMenu; + SEVENT_END() + + SEVENT_BEGIN_EX(EventPopMenu, EVT_POPMENU, on_pop_menu, SOUI_EXP) + int m_index; + SMenu* m_pMenu; SEVENT_END() //class SOUI_EXP EventSelectMenu : public TplEventArgs //{ diff --git a/SOUI/src/control/SMenuBar.cpp b/SOUI/src/control/SMenuBar.cpp index 51b140002fd782d43f46db17cd4e90abb706c4ad..88ac145f26b34d02f988af19e5dd4ddd3e223f3c 100644 --- a/SOUI/src/control/SMenuBar.cpp +++ b/SOUI/src/control/SMenuBar.cpp @@ -3,78 +3,101 @@ #define TIMER_POP 10 + namespace SOUI { - static const wchar_t XmlBtnStyle[] = L"btnStyle"; - static const wchar_t XmlMenus[] = L"menus"; + HHOOK SMenuBar::m_hMsgHook = NULL; + SMenuBar* SMenuBar::m_pMenuBar = NULL; + + const TCHAR XmlBtnStyle[] = _T("btnStyle"); + const TCHAR XmlMenus[] = _T("menus"); - class SMenuItem : - public SButton + class SMenuItem : + public SButton, + public SMenu { SOUI_CLASS_NAME(SMenuItem, L"menuItem") - friend class SMenuBar; + friend class SMenuBar; public: - SMenuItem(SMenuBar *pHostMenu, const SStringT &strResName); + SMenuItem(SMenuBar* pHostMenu); ~SMenuItem(); void SetData(ULONG_PTR data) { m_data = data; } ULONG_PTR GetData() { return m_data; } + bool IsMenuLoaded() const; protected: - BOOL IsLoad() { return m_bIsLoad; } UINT PopMenu(); + HRESULT OnAttrSrc(const SStringW& strValue, BOOL bLoading); + virtual void OnFinalRelease() { delete this; } - void OnMouseMove(UINT nFlags, CPoint pt); - void OnLButtonUp(UINT nFlags, CPoint pt); - void OnLButtonDown(UINT nFlags, CPoint pt); + virtual CSize GetDesiredSize(LPCRECT pRcContainer); + bool OnCmd(EventArgs* e); - void OnTimer(char timerID); + void OnTimer(UINT_PTR timerID); SOUI_MSG_MAP_BEGIN() - MSG_WM_MOUSEMOVE(OnMouseMove) - MSG_WM_LBUTTONDOWN(OnLButtonDown) - MSG_WM_LBUTTONUP(OnLButtonUp) - MSG_WM_TIMER_EX(OnTimer) - SOUI_MSG_MAP_END() + MSG_WM_TIMER(OnTimer) + SOUI_MSG_MAP_END() - SOUI_ATTRS_BEGIN() - ATTR_STRINGT(L"menuName", m_strResName, TRUE) - SOUI_ATTRS_END() + SOUI_ATTRS_BEGIN() + ATTR_CUSTOM(_T("src"), OnAttrSrc) + SOUI_ATTRS_END() - ULONG_PTR m_data; + ULONG_PTR m_data; SMenuBar* m_pHostMenu; - SStringT m_strResName; - BOOL m_bIsLoad; - SMenuEx m_Menu; + BOOL m_bIsRegHotKey; + int m_iIndex; + TCHAR m_cAccessKey; }; - SMenuItem::SMenuItem(SMenuBar *pHostMenu, const SStringT & strResName): + SMenuItem::SMenuItem(SMenuBar* pHostMenu) : m_data(0), m_pHostMenu(pHostMenu), - m_strResName(strResName), - m_bIsLoad(FALSE) + m_bIsRegHotKey(FALSE), + m_iIndex(-1), + m_cAccessKey(0) { - SetAttribute(L"drawFocusRect", L"0"); - m_bIsLoad = m_Menu.LoadMenu(strResName); + m_bDrawFocusRect = FALSE; + GetEventSet()->subscribeEvent(EventCmd::EventID, Subscriber(&SMenuItem::OnCmd, this)); } SMenuItem::~SMenuItem() { } + bool SMenuItem::IsMenuLoaded() const + { + return true; + } + UINT SMenuItem::PopMenu() { + if (m_pHostMenu->m_pNowMenu != NULL) + return 0; m_pHostMenu->m_bIsShow = TRUE; m_pHostMenu->m_pNowMenu = this; + m_pHostMenu->m_iNowMenu = m_iIndex; + + // 把弹出事件发送过去 + EventPopMenu evt_pop(m_pHostMenu); + evt_pop.m_index = m_iIndex; + evt_pop.m_pMenu = this; + FireEvent(evt_pop); SetCheck(TRUE); CRect rcHost; ::GetWindowRect(m_pHostMenu->m_hWnd, rcHost); CRect rcMenu = GetClientRect(); + + if (SMenuBar::m_hMsgHook == NULL) + SMenuBar::m_hMsgHook = ::SetWindowsHookEx(WH_MSGFILTER, + SMenuBar::MenuSwitch, NULL, GetCurrentThreadId());// m_bLoop may become TRUE + int iRet = 0; - iRet = m_Menu.TrackPopupMenu(TPM_RETURNCMD, + iRet = TrackPopupMenu(TPM_RETURNCMD, rcHost.left + rcMenu.left, rcHost.top + rcMenu.bottom + 2, m_pHostMenu->m_hWnd); SetCheck(FALSE); @@ -82,45 +105,49 @@ namespace SOUI if (m_pHostMenu->m_pNowMenu != this || iRet == 0) { m_pHostMenu->m_pNowMenu = NULL; + m_pHostMenu->m_iNowMenu = -1; return iRet; } + m_pHostMenu->m_iNowMenu = -1; m_pHostMenu->m_pNowMenu = NULL; + // uninstall hook + ::UnhookWindowsHookEx(SMenuBar::m_hMsgHook); + SMenuBar::m_hMsgHook = NULL; + // 把选择事件发送过去 - EventSelectMenu evt(m_pHostMenu); - evt.m_id = iRet; - evt.m_pMenu = &m_Menu; - FireEvent(evt); + EventSelectMenu evt_sel(m_pHostMenu); + evt_sel.m_id = iRet; + evt_sel.m_pMenu = this; + FireEvent(evt_sel); return iRet; } - void SMenuItem::OnMouseMove(UINT nFlags, CPoint pt) + HRESULT SMenuItem::OnAttrSrc(const SStringW& strValue, BOOL bLoading) { - CRect rcWnd = GetWindowRect(); - if (m_pHostMenu->m_bIsShow && m_pHostMenu->m_pNowMenu != this) - { - m_pHostMenu->m_pNowMenu = this; - SMenuEx::ExitPopupMenu(); - SetTimer(TIMER_POP, 10); - } + return LoadMenu(strValue) ? S_OK : E_INVALIDARG; } - void SMenuItem::OnLButtonUp(UINT nFlags, CPoint pt) + CSize SMenuItem::GetDesiredSize(LPCRECT pRcContainer) { - SButton::OnLButtonUp(nFlags, pt); + CSize size = SWindow::GetDesiredSize(pRcContainer); + size.cx += 13; + size.cy += 3; + return size; } - void SMenuItem::OnLButtonDown(UINT nFlags, CPoint pt) + bool SMenuItem::OnCmd(EventArgs* e) { - SButton::OnLButtonDown(nFlags, pt); - BringWindowToTop(); if (!::IsWindow(m_pHostMenu->m_hWnd)) - return; + m_pHostMenu->m_hWnd = GetContainer()->GetHostHwnd(); + + e->bubbleUp = false; PopMenu(); + return true; } - void SMenuItem::OnTimer(char timerID) + void SMenuItem::OnTimer(UINT_PTR timerID) { if (timerID == TIMER_POP) { @@ -135,51 +162,117 @@ namespace SOUI SMenuBar::SMenuBar() : m_bIsShow(FALSE), m_hWnd(NULL), - m_pNowMenu(NULL) + m_pNowMenu(NULL), + m_iNowMenu(-1) { m_evtSet.addEvent(EVENTID(EventSelectMenu)); + SMenuBar::m_pMenuBar = this; } SMenuBar::~SMenuBar() { - } - BOOL SMenuBar::Init(SHostWnd * pHostWnd) - { - if (::IsWindow(pHostWnd->m_hWnd)) + if (SMenuBar::m_hMsgHook) { - m_hWnd = pHostWnd->m_hWnd; - return TRUE; + ::UnhookWindowsHookEx(SMenuBar::m_hMsgHook); + SMenuBar::m_hMsgHook = NULL; } - return FALSE; } BOOL SMenuBar::Insert(LPCTSTR pszTitle, LPCTSTR pszResName, int iPos) { if (!pszResName) return FALSE; - SMenuItem *pNewMenu = new SMenuItem(this, pszResName); + SMenuItem* pNewMenu = new SMenuItem(this); SASSERT(pNewMenu); - if (!pNewMenu->IsLoad()) + InsertChild(pNewMenu); + + pugi::xml_node xmlBtnStyle = m_xmlStyle.child(XmlBtnStyle); + if (xmlBtnStyle) + pNewMenu->InitFromXml(xmlBtnStyle); + + if (pszTitle) + pNewMenu->SetWindowText(pszTitle); + + pNewMenu->SetAttribute(L"src", S_CT2W(pszResName)); + pNewMenu->SetWindowText(pszTitle); + + if (!pNewMenu->IsMenuLoaded()) { - pNewMenu->DestroyWindow(); - delete pNewMenu; + DestroyChild(pNewMenu); return FALSE; } + + SStringT strText = pszTitle; + int nPos = strText.ReverseFind('&'); + if (nPos > -1) + pNewMenu->SetAttribute(_T("accel"), SStringT().Format(_T("alt+%c"), strText[nPos + 1])); + + if (iPos < 0) iPos = m_lstMenuItem.GetCount(); + m_lstMenuItem.InsertAt(iPos, pNewMenu); + + pNewMenu->m_iIndex = iPos; + for (size_t i = iPos + 1; i < m_lstMenuItem.GetCount(); i++) + { + m_lstMenuItem[i]->m_iIndex++; + } + return TRUE; + } + + BOOL SMenuBar::Insert(pugi::xml_node xmlNode, int iPos) + { + SMenuItem* pNewMenu = new SMenuItem(this); + SASSERT(pNewMenu); InsertChild(pNewMenu); pugi::xml_node xmlBtnStyle = m_xmlStyle.child(XmlBtnStyle); if (xmlBtnStyle) pNewMenu->InitFromXml(xmlBtnStyle); - if(pszTitle) - pNewMenu->SetWindowText(pszTitle); + pNewMenu->InitFromXml(xmlNode); + if (!pNewMenu->IsMenuLoaded()) + { + DestroyChild(pNewMenu); + return FALSE; + } + + SStringT strText = xmlNode.first_child().value(); + int nPos = strText.ReverseFind('&'); + if (nPos > -1) + pNewMenu->SetAttribute(_T("accel"), SStringT().Format(_T("alt+%c"), strText[nPos + 1])); - if (iPos < 0) iPos = (int)m_lstMenuItem.GetCount(); + if (iPos < 0) iPos = m_lstMenuItem.GetCount(); m_lstMenuItem.InsertAt(iPos, pNewMenu); - UpdateChildrenPosition(); + pNewMenu->m_iIndex = iPos; + for (size_t i = iPos + 1; i < m_lstMenuItem.GetCount(); i++) + { + m_lstMenuItem[i]->m_iIndex++; + } return TRUE; } + SMenu* SMenuBar::GetMenu(DWORD dwPos) + { + if (dwPos >= m_lstMenuItem.GetCount()) + return NULL; + return m_lstMenuItem[dwPos]; + } + int SMenuBar::HitTest(CPoint pt) + { + for (size_t i = 0; i < m_lstMenuItem.GetCount(); i++) + { + SMenuItem* pItem = m_lstMenuItem[i]; + CRect rcItem = pItem->GetClientRect(); + if (rcItem.PtInRect(pt)) + return i; + } + return -1; + } + SMenuItem* SMenuBar::GetMenuItem(DWORD dwPos) + { + if (dwPos >= m_lstMenuItem.GetCount()) + return NULL; + return m_lstMenuItem[dwPos]; + } BOOL SMenuBar::CreateChildren(pugi::xml_node xmlNode) { pugi::xml_node xmlBtnStyle = xmlNode.child(XmlBtnStyle); @@ -192,35 +285,84 @@ namespace SOUI { for (pugi::xml_node xmlChild = xmlTMenus.first_child(); xmlChild; xmlChild = xmlChild.next_sibling()) { - if (wcscmp(xmlChild.name(), SMenuItem::GetClassName()) != 0) + if (_tcsicmp(xmlChild.name(), SMenuItem::GetClassName()) != 0) continue; - Insert(S_CW2T(xmlChild.first_child().value()), - S_CW2T(xmlChild.attribute(L"menuName").value())); + Insert(xmlChild); } } return TRUE; } - void SMenuBar::UpdateChildrenPosition() + + LRESULT SMenuBar::MenuSwitch(int code, WPARAM wParam, LPARAM lParam) { - CRect rcClient; - GetClientRect(&rcClient); - for (size_t i = 0; i < m_lstMenuItem.GetCount(); i++) + if (code == MSGF_MENU) { - CRect rcLeft; - if (i > 0) - m_lstMenuItem[i - 1]->GetWindowRect(&rcLeft); - else + MSG msg = *(MSG*)lParam; + int nMsg = msg.message; + switch (nMsg) + { + case WM_MOUSEMOVE: { - rcLeft = rcClient; - rcLeft.right = rcLeft.left; + CPoint pt = msg.lParam; + if (SMenuBar::m_pMenuBar->m_ptMouse != pt && + SMenuBar::m_pMenuBar->m_iNowMenu != -1) + { + SMenuBar::m_pMenuBar->m_ptMouse = pt; + ::ScreenToClient(SMenuBar::m_pMenuBar->m_hWnd, &pt); + int nIndex = SMenuBar::m_pMenuBar->HitTest(pt); + if (nIndex != -1) + { + SMenuItem* menuItem = SMenuBar::m_pMenuBar->GetMenuItem(nIndex); + if (menuItem && SMenuBar::m_pMenuBar->m_iNowMenu != nIndex) + { + SMenuBar::m_pMenuBar->m_pNowMenu = menuItem; + SMenuBar::m_pMenuBar->m_iNowMenu = nIndex; + ::PostMessage(msg.hwnd, WM_CANCELMODE, 0, 0); + menuItem->SetTimer(TIMER_POP, 10); + return TRUE; + } + } + } + break; + } + case WM_KEYDOWN: + { + TCHAR vKey = msg.wParam; + if (SMenuBar::m_pMenuBar->m_iNowMenu == -1) + return TRUE; + if (vKey == VK_LEFT) + { + int nRevIndex = SMenuBar::m_pMenuBar->m_iNowMenu - 1; + if (nRevIndex < 0) nRevIndex = SMenuBar::m_pMenuBar->m_lstMenuItem.GetCount() - 1; + SMenuItem* menuItem = SMenuBar::m_pMenuBar->m_lstMenuItem[nRevIndex]; + if (menuItem) + { + SMenuBar::m_pMenuBar->m_pNowMenu = menuItem; + SMenuBar::m_pMenuBar->m_iNowMenu = nRevIndex; + ::PostMessage(SMenuBar::m_pMenuBar->m_hWnd, WM_KEYDOWN, VK_ESCAPE, 0); + menuItem->SetTimer(TIMER_POP, 10); + return TRUE; + } + } + else if (vKey == VK_RIGHT) + { + int nNextIndex = SMenuBar::m_pMenuBar->m_iNowMenu + 1; + if (nNextIndex >= (int)SMenuBar::m_pMenuBar->m_lstMenuItem.GetCount()) nNextIndex = 0; + SMenuItem* menuItem = SMenuBar::m_pMenuBar->GetMenuItem(nNextIndex); + if (menuItem) + { + SMenuBar::m_pMenuBar->m_pNowMenu = menuItem; + SMenuBar::m_pMenuBar->m_iNowMenu = nNextIndex; + ::PostMessage(SMenuBar::m_pMenuBar->m_hWnd, WM_KEYDOWN, VK_ESCAPE, 0); + ::PostMessage(msg.hwnd, WM_CANCELMODE, 0, 0); + menuItem->SetTimer(TIMER_POP, 10); + return TRUE; + } + } + } } - CRect rcInit; - rcInit.top = rcLeft.top; - rcInit.left = rcLeft.right + 1; - rcInit.right = rcLeft.right + 50; - rcInit.bottom = rcInit.top + 20; - m_lstMenuItem[i]->Move(rcInit); } + return CallNextHookEx(m_hMsgHook, code, wParam, lParam); } } \ No newline at end of file