menu.cpp
来自「A*算法 A*算法 A*算法 A*算法A*算法A*算法」· C++ 代码 · 共 2,300 行 · 第 1/5 页
CPP
2,300 行
{
wxLogLastError(_T("SetWindowPos(HWND_TOP)"));
}
Refresh();
}
#endif // __WXMSW__
}
void wxPopupMenuWindow::Dismiss()
{
if ( HasOpenSubmenu() )
{
wxMenuItem *item = GetCurrentItem();
wxCHECK_RET( item && item->IsSubMenu(), _T("where is our open submenu?") );
wxPopupMenuWindow *win = item->GetSubMenu()->m_popupMenu;
wxCHECK_RET( win, _T("opened submenu is not opened?") );
win->Dismiss();
OnSubmenuDismiss( false );
}
wxPopupTransientWindow::Dismiss();
ResetCurrent();
}
void wxPopupMenuWindow::OnDismiss()
{
// when we are dismissed because the user clicked elsewhere or we lost
// focus in any other way, hide the parent menu as well
HandleDismiss(true);
}
void wxPopupMenuWindow::OnSubmenuDismiss(bool WXUNUSED(dismissParent))
{
m_hasOpenSubMenu = false;
}
void wxPopupMenuWindow::HandleDismiss(bool dismissParent)
{
m_menu->OnDismiss(dismissParent);
}
void wxPopupMenuWindow::DismissAndNotify()
{
Dismiss();
HandleDismiss(true);
}
// ----------------------------------------------------------------------------
// wxPopupMenuWindow geometry
// ----------------------------------------------------------------------------
wxMenuItemList::compatibility_iterator
wxPopupMenuWindow::GetMenuItemFromPoint(const wxPoint& pt) const
{
// we only use the y coord normally, but still check x in case the point is
// outside the window completely
if ( wxWindow::HitTest(pt) == wxHT_WINDOW_INSIDE )
{
wxCoord y = 0;
for ( wxMenuItemList::compatibility_iterator node = m_menu->GetMenuItems().GetFirst();
node;
node = node->GetNext() )
{
wxMenuItem *item = node->GetData();
y += item->GetHeight();
if ( y > pt.y )
{
// found
return node;
}
}
}
#if wxUSE_STL
return wxMenuItemList::compatibility_iterator();
#else
return NULL;
#endif
}
// ----------------------------------------------------------------------------
// wxPopupMenuWindow drawing
// ----------------------------------------------------------------------------
void wxPopupMenuWindow::RefreshItem(wxMenuItem *item)
{
wxCHECK_RET( item, _T("can't refresh NULL item") );
wxASSERT_MSG( IsShown(), _T("can't refresh menu which is not shown") );
// FIXME: -1 here because of SetLogicalOrigin(1, 1) in DoDraw()
RefreshRect(wxRect(0, item->GetPosition() - 1,
m_menu->GetGeometryInfo().GetSize().x, item->GetHeight()));
}
void wxPopupMenuWindow::DoDraw(wxControlRenderer *renderer)
{
// no clipping so far - do we need it? I don't think so as the menu is
// never partially covered as it is always on top of everything
wxDC& dc = renderer->GetDC();
dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
// FIXME: this should be done in the renderer, however when it is fixed
// wxPopupMenuWindow::RefreshItem() should be changed too!
dc.SetLogicalOrigin(1, 1);
wxRenderer *rend = renderer->GetRenderer();
wxCoord y = 0;
const wxMenuGeometryInfo& gi = m_menu->GetGeometryInfo();
for ( wxMenuItemList::compatibility_iterator node = m_menu->GetMenuItems().GetFirst();
node;
node = node->GetNext() )
{
wxMenuItem *item = node->GetData();
if ( item->IsSeparator() )
{
rend->DrawMenuSeparator(dc, y, gi);
}
else // not a separator
{
int flags = 0;
if ( item->IsCheckable() )
{
flags |= wxCONTROL_CHECKABLE;
if ( item->IsChecked() )
{
flags |= wxCONTROL_CHECKED;
}
}
if ( !item->IsEnabled() )
flags |= wxCONTROL_DISABLED;
if ( item->IsSubMenu() )
flags |= wxCONTROL_ISSUBMENU;
if ( item == GetCurrentItem() )
flags |= wxCONTROL_SELECTED;
wxBitmap bmp;
if ( !item->IsEnabled() )
{
bmp = item->GetDisabledBitmap();
}
if ( !bmp.Ok() )
{
// strangely enough, for unchecked item we use the
// "checked" bitmap because this is the default one - this
// explains this strange boolean expression
bmp = item->GetBitmap(!item->IsCheckable() || item->IsChecked());
}
rend->DrawMenuItem
(
dc,
y,
gi,
item->GetLabel(),
item->GetAccelString(),
bmp,
flags,
item->GetAccelIndex()
);
}
y += item->GetHeight();
}
}
// ----------------------------------------------------------------------------
// wxPopupMenuWindow actions
// ----------------------------------------------------------------------------
void wxPopupMenuWindow::ClickItem(wxMenuItem *item)
{
wxCHECK_RET( item, _T("can't click NULL item") );
wxASSERT_MSG( !item->IsSeparator() && !item->IsSubMenu(),
_T("can't click this item") );
wxMenu* menu = m_menu;
// close all menus
DismissAndNotify();
menu->ClickItem(item);
}
void wxPopupMenuWindow::OpenSubmenu(wxMenuItem *item, InputMethod how)
{
wxCHECK_RET( item, _T("can't open NULL submenu") );
wxMenu *submenu = item->GetSubMenu();
wxCHECK_RET( submenu, _T("can only open submenus!") );
// FIXME: should take into account the border width
submenu->Popup(ClientToScreen(wxPoint(0, item->GetPosition())),
wxSize(m_menu->GetGeometryInfo().GetSize().x, 0),
how == WithKeyboard /* preselect first item then */);
m_hasOpenSubMenu = true;
}
bool wxPopupMenuWindow::ActivateItem(wxMenuItem *item, InputMethod how)
{
// don't activate disabled items
if ( !item || !item->IsEnabled() )
{
return false;
}
// normal menu items generate commands, submenus can be opened and
// the separators don't do anything
if ( item->IsSubMenu() )
{
OpenSubmenu(item, how);
}
else if ( !item->IsSeparator() )
{
ClickItem(item);
}
else // separator, can't activate
{
return false;
}
return true;
}
// ----------------------------------------------------------------------------
// wxPopupMenuWindow input handling
// ----------------------------------------------------------------------------
bool wxPopupMenuWindow::ProcessLeftDown(wxMouseEvent& event)
{
// wxPopupWindowHandler dismisses the window when the mouse is clicked
// outside it which is usually just fine, but there is one case when we
// don't want to do it: if the mouse was clicked on the parent submenu item
// which opens this menu, so check for it
wxPoint pos = event.GetPosition();
if ( HitTest(pos.x, pos.y) == wxHT_WINDOW_OUTSIDE )
{
wxMenu *menu = m_menu->GetParent();
if ( menu )
{
wxPopupMenuWindow *win = menu->m_popupMenu;
wxCHECK_MSG( win, false, _T("parent menu not shown?") );
pos = ClientToScreen(pos);
if ( win->GetMenuItemFromPoint(win->ScreenToClient(pos)) )
{
// eat the event
return true;
}
//else: it is outside the parent menu as well, do dismiss this one
}
}
return false;
}
void wxPopupMenuWindow::OnLeftUp(wxMouseEvent& event)
{
wxMenuItemList::compatibility_iterator node = GetMenuItemFromPoint(event.GetPosition());
if ( node )
{
ActivateItem(node->GetData(), WithMouse);
}
}
void wxPopupMenuWindow::OnMouseMove(wxMouseEvent& event)
{
const wxPoint pt = event.GetPosition();
// we need to ignore extra mouse events: example when this happens is when
// the mouse is on the menu and we open a submenu from keyboard - Windows
// then sends us a dummy mouse move event, we (correctly) determine that it
// happens in the parent menu and so immediately close the just opened
// submenu!
#ifdef __WXMSW__
static wxPoint s_ptLast;
wxPoint ptCur = ClientToScreen(pt);
if ( ptCur == s_ptLast )
{
return;
}
s_ptLast = ptCur;
#endif // __WXMSW__
ProcessMouseMove(pt);
event.Skip();
}
void wxPopupMenuWindow::ProcessMouseMove(const wxPoint& pt)
{
wxMenuItemList::compatibility_iterator node = GetMenuItemFromPoint(pt);
// don't reset current to NULL here, we only do it when the mouse leaves
// the window (see below)
if ( node )
{
if ( node != m_nodeCurrent )
{
ChangeCurrent(node);
wxMenuItem *item = GetCurrentItem();
if ( CanOpen(item) )
{
OpenSubmenu(item, WithMouse);
}
}
//else: same item, nothing to do
}
else // not on an item
{
// the last open submenu forwards the mouse move messages to its
// parent, so if the mouse moves to another item of the parent menu,
// this menu is closed and this other item is selected - in the similar
// manner, the top menu forwards the mouse moves to the menubar which
// allows to select another top level menu by just moving the mouse
// we need to translate our client coords to the client coords of the
// window we forward this event to
wxPoint ptScreen = ClientToScreen(pt);
// if the mouse is outside this menu, let the parent one to
// process it
wxMenu *menuParent = m_menu->GetParent();
if ( menuParent )
{
wxPopupMenuWindow *win = menuParent->m_popupMenu;
// if we're shown, the parent menu must be also shown
wxCHECK_RET( win, _T("parent menu is not shown?") );
win->ProcessMouseMove(win->ScreenToClient(ptScreen));
}
else // no parent menu
{
wxMenuBar *menubar = m_menu->GetMenuBar();
if ( menubar )
{
if ( menubar->ProcessMouseEvent(
menubar->ScreenToClient(ptScreen)) )
{
// menubar has closed this menu and opened another one, probably
return;
}
}
}
//else: top level popup menu, no other processing to do
}
}
void wxPopupMenuWindow::OnMouseLeave(wxMouseEvent& event)
{
// due to the artefact of mouse events generation under MSW, we actually
// may get the mouse leave event after the menu had been already dismissed
// and calling ChangeCurrent() would then assert, so don't do it
if ( IsShown() )
{
// we shouldn't change the current them if our submenu is opened and
// mouse moved there, in this case the submenu is responsable for
// handling it
bool resetCurrent;
if ( HasOpenSubmenu() )
{
wxMenuItem *item = GetCurrentItem();
wxCHECK_RET( CanOpen(item), _T("where is our open submenu?") );
wxPopupMenuWindow *win = item->GetSubMenu()->m_popupMenu;
wxCHECK_RET( win, _T("submenu is opened but not shown?") );
// only handle this event if the mouse is not inside the submenu
wxPoint pt = ClientToScreen(event.GetPosition());
resetCurrent =
win->HitTest(win->ScreenToClient(pt)) == wxHT_WINDOW_OUTSIDE;
}
else
{
// this menu is the last opened
resetCurrent = true;
}
if ( resetCurrent )
{
#if wxUSE_STL
ChangeCurrent(wxMenuItemList::compatibility_iterator());
#else
ChangeCurrent(NULL);
#endif
}
}
event.Skip();
}
void wxPopupMenuWindow::OnKeyDown(wxKeyEvent& event)
{
wxMenuBar *menubar = m_menu->GetMenuBar();
if ( menubar )
{
menubar->ProcessEvent(event);
}
else if ( !ProcessKeyDown(event.GetKeyCode()) )
{
event.Skip();
}
}
bool wxPopupMenuWindow::ProcessKeyDown(int key)
{
wxMenuItem *item = GetCurrentItem();
// first let the opened submenu to have it (no test for IsEnabled() here,
// the keys navigate even in a disabled submenu if we had somehow managed
// to open it inspit of this)
if ( HasOpenSubmenu() )
{
wxCHECK_MSG( CanOpen(item), false,
_T("has open submenu but another item selected?") );
if ( item->GetSubMenu()->ProcessKeyDown(key) )
return true;
}
bool processed = true;
// handle the up/down arrows, home, end, esc and return here, pass the
// left/right arrows to the menu bar except when the right arrow can be
// used to open a submenu
switch ( key )
{
case WXK_LEFT:
// if we're not a top level menu, close us, else leave this to the
// menubar
if ( !m_menu->GetParent() )
{
processed = false;
break;
}
// fall through
case WXK_ESCAPE:
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?