📄 menubar.cpp
字号:
//only when first level pop menu, left key can process by cmenubar
m_bProcessLeftArrow = (::GetSubMenu(m_hmenu, m_iPopupTracking)==hmenu ? 1 : 0);
}
}
// globals--yuk! But no other way using windows hooks.
//
static CMenuBar* g_pMenuBar = NULL;
static HHOOK g_hMsgHook = NULL;
////////////////
// Menu filter hook just passes to virtual CMenuBar function
//
LRESULT CALLBACK
CMenuBar::MenuInputFilter(int code, WPARAM wp, LPARAM lp)
{
return (code==MSGF_MENU && g_pMenuBar &&
g_pMenuBar->OnMenuInput(*((MSG*)lp))) ? TRUE
: CallNextHookEx(g_hMsgHook, code, wp, lp);
}
//////////////////
// Handle menu input event: Look for left/right to change popup menu,
// mouse movement over over a different menu button for "hot" popup effect.
// Returns TRUE if message handled (to eat it).
//
BOOL CMenuBar::OnMenuInput(MSG& m)
{
ASSERT_VALID(this);
ASSERT(m_iTrackingState == TRACK_POPUP); // sanity check
int msg = m.message;
if (msg==WM_KEYDOWN)
{
// handle left/right-arow.
TCHAR vkey = m.wParam;
if ((vkey == VK_LEFT && m_bProcessLeftArrow) || (vkey == VK_RIGHT && m_bProcessRightArrow))
{
CancelMenuAndTrackNewOne( GetNextOrPrevButton(m_iPopupTracking, vkey==VK_LEFT));
return TRUE; // eat it
}
else if (vkey == VK_ESCAPE)
{
m_bEscapeWasPressed = TRUE; // (menu will abort itself)
}
}
else if (msg==WM_MOUSEMOVE || msg==WM_LBUTTONDOWN)
{
// handle mouse move or click
CPoint pt = m.lParam;
ScreenToClient(&pt);
if((msg==WM_MOUSEMOVE || msg==WM_LBUTTONDOWN) && g_nDelay>=0)
g_nDelay--;
if (msg == WM_MOUSEMOVE && !g_bNoHook && g_nDelay<0)
{
if (pt != m_ptMouse )
{
int iButton = HitTest(pt);
if (IsValidButton(iButton) && iButton != m_iPopupTracking)
{
if (::WindowFromPoint((CPoint)m.lParam)==this->m_hWnd)
{
// user moved mouse over a different button: track its popup
CancelMenuAndTrackNewOne(iButton);
m_iPopupTracking = iButton;
}
}
m_ptMouse = pt;
}
}
else if (msg == WM_LBUTTONDOWN && !g_bNoHook && g_nDelay<0)
{
if (HitTest(pt) == m_iPopupTracking)
{
if (::WindowFromPoint((CPoint)m.lParam)==this->m_hWnd)
{
// user clicked on same button I am tracking: cancel menu
CancelMenuAndTrackNewOne(-1);
return TRUE; // eat it
}
}
}
}
return FALSE; // not handled
}
//////////////////
// Cancel the current popup menu by posting WM_CANCELMODE, and track a new
// menu. iNewPopup is which new popup to track (-1 to quit).
//
void CMenuBar::CancelMenuAndTrackNewOne(int iNewPopup)
{
ASSERT_VALID(this);
if (iNewPopup != m_iPopupTracking)
{
AfxGetApp()->m_pMainWnd->PostMessage(WM_CANCELMODE); // quit menu loop
m_iNewPopup = iNewPopup; // go to this popup (-1 = quit)
}
}
//////////////////
// Track the popup submenu associated with the i'th button in the menu bar.
// This fn actually goes into a loop, tracking different menus until the user
// selects a command or exits the menu.
//
void CMenuBar::TrackPopup(int iButton)
{
ASSERT_VALID(this);
ASSERT(m_hmenu);
g_bRTab = FALSE;
CMenu menu;
menu.Attach(m_hmenu);
int nMenuItems = menu.GetMenuItemCount();
while (iButton >= 0)// while user selects another menu
{
m_iNewPopup = -1; // assume quit after this
GetToolBarCtrl().PressButton(iButton, TRUE); // press the button
UpdateWindow(); // and force repaint now
// post a simulated arrow-down into the message stream
// so TrackPopupMenu will read it and move to the first item
AfxGetApp()->m_pMainWnd->PostMessage(WM_KEYDOWN, VK_DOWN, 1);
AfxGetApp()->m_pMainWnd->PostMessage(WM_KEYUP, VK_DOWN, 1);
SetTrackingState(TRACK_POPUP, iButton); // enter tracking state
// Need to install a hook to trap menu input in order to make
// left/right-arrow keys and "hot" mouse tracking work.
//
g_pMenuBar = this;
g_hMsgHook = SetWindowsHookEx(WH_MSGFILTER,
MenuInputFilter, NULL, ::GetCurrentThreadId());
if(g_hMsgHook==NULL)
g_hMsgHook = SetWindowsHookEx(WH_MSGFILTER,
MenuInputFilter, NULL, ::GetCurrentThreadId());
// get submenu and display it beneath button
TPMPARAMS tpm;
CRect rcButton;
GetToolBarCtrl().GetRect(iButton, rcButton);
ClientToScreen(&rcButton);
CPoint pt = ComputeMenuTrackPoint(rcButton, tpm);
HMENU hMenuPopup = ::GetSubMenu(m_hmenu, iButton);
ASSERT(hMenuPopup);
//CToolMenu submenu;
submenu.Detach();
submenu.m_pImg = &m_ImgList;
submenu.Attach(hMenuPopup);
BOOL bRet = submenu.TrackPopupMenu(TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_VERTICAL,pt.x, pt.y, AfxGetApp()->m_pMainWnd);
// uninstall hook.
::UnhookWindowsHookEx(g_hMsgHook);
g_hMsgHook = NULL;
g_pMenuBar = NULL;
GetToolBarCtrl().PressButton(iButton, FALSE); // un-press button
UpdateWindow(); // and force repaint now
// If the user exited the menu loop by pressing Escape,
// return to track-button state; otherwise normal non-tracking state.
SetTrackingState(m_bEscapeWasPressed ?
TRACK_BUTTON : TRACK_NONE, iButton);
// If the user moved mouse to a new top-level popup (eg from File to
// Edit button), I will have posted a WM_CANCELMODE to quit
// the first popup, and set m_iNewPopup to the new menu to show.
// Otherwise, m_iNewPopup will be -1 as set above.
// So just set iButton to the next popup menu and keep looping...
iButton = m_iNewPopup;
//submenu.Detach();
}
menu.Detach();
}
//////////////////
// Given button rectangle, compute point and "exclude rect" for
// TrackPopupMenu, based on current docking style, so that the menu will
// appear always inside the window.
//
CPoint CMenuBar::ComputeMenuTrackPoint(const CRect& rcButn, TPMPARAMS& tpm)
{
tpm.cbSize = sizeof(tpm);
DWORD dwStyle = m_dwStyle;
CPoint pt;
CRect& rcExclude = (CRect&)tpm.rcExclude;
rcExclude = rcButn;
::GetWindowRect(::GetDesktopWindow(), &rcExclude);
return CPoint(rcButn.left, rcButn.bottom);
}
//////////////////
// This function translates special menu keys and mouse actions.
// You must call it from your frame's PreTranslateMessage.
//
BOOL CMenuBar::TranslateFrameMessage(MSG* pMsg)
{
ASSERT_VALID(this);
ASSERT(pMsg);
try{
UINT msg = pMsg->message;
if (WM_LBUTTONDOWN <= msg && msg <= WM_MOUSELAST)
{
if (pMsg->hwnd != m_hWnd && m_iTrackingState > 0)
{
// user clicked outside menu bar: exit tracking mode
SetTrackingState(TRACK_NONE);
}
}
else if (msg==WM_SYSKEYDOWN || msg==WM_SYSKEYUP || msg==WM_KEYDOWN)
{
BOOL bAlt = HIWORD(pMsg->lParam) & KF_ALTDOWN; // Alt key down
TCHAR vkey = pMsg->wParam; // get virt key
// key is VK_MENU or F10 with no alt/ctrl/shift: toggle menu mode
if (vkey==VK_MENU )
{
// key is VK_MENU : toggle menu mode
if (msg==WM_SYSKEYUP)
{
ToggleTrackButtonMode();
}
return TRUE;
}
else if ((msg==WM_SYSKEYDOWN || msg==WM_KEYDOWN))
{
if (m_iTrackingState == TRACK_BUTTON)
{
// I am tracking: handle left/right/up/down/space/Esc
switch (vkey)
{
case VK_LEFT:
case VK_RIGHT:
// left or right-arrow: change hot button if tracking buttons
GetToolBarCtrl().SetHotItem(GetNextOrPrevButton(GetToolBarCtrl().GetHotItem(), vkey==VK_LEFT));
return TRUE;
case VK_SPACE: // (personally, I like SPACE to enter menu too)
case VK_UP:
case VK_DOWN:
// up or down-arrow: move into current menu, if any
TrackPopup(GetToolBarCtrl().GetHotItem());
return TRUE;
case VK_ESCAPE:
// escape key: exit tracking mode
SetTrackingState(TRACK_NONE);
return TRUE;
}
}
// Handle alphanumeric key: invoke menu. Note that Alt-X
// chars come through as WM_SYSKEYDOWN, plain X as WM_KEYDOWN.
if ((bAlt || m_iTrackingState == TRACK_BUTTON) && isalnum(vkey))
{
// Alt-X, or else X while in tracking mode
UINT nID;
if (MapAccelerator(vkey, nID))
{
TrackPopup(nID); // found menu mnemonic: track it
return TRUE; // handled
}
else if (m_iTrackingState==TRACK_BUTTON && !bAlt)
{
return TRUE;
}
}
// Default for any key not handled so far: return to no-menu state
if (m_iTrackingState > 0)
{
SetTrackingState(TRACK_NONE);
}
}
}
}catch(...){}
return FALSE; // not handled, pass along
}
#ifdef _DEBUG
void CMenuBar::AssertValid() const
{
}
void CMenuBar::Dump(CDumpContext& dc) const
{
}
#endif
BOOL CMenuBar::MapAccelerator(TCHAR ch, UINT & nID)
{
return SendMessage(TB_MAPACCELERATOR, (WPARAM)ch, (LPARAM)&nID);
}
UINT CMenuBar::OnNcHitTest(CPoint point)
{
// TODO: Add your message handler code here and/or call default
if(m_iTrackingState == TRACK_POPUP)
{
ScreenToClient(&point);
int iButton = HitTest(point);
if (IsValidButton(iButton) && iButton != m_iPopupTracking)
{
// user moved mouse over a different button: track its popup
CancelMenuAndTrackNewOne(iButton);
m_iPopupTracking = iButton;
}
m_ptMouse = point;
}
return CToolBar::OnNcHitTest(point);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -