* Fix keyboard navigation and hottracking behaviour. One glitch remains where quickly moving the mouse to a parent's toolbar item, and returning to the submenu before it closes, won't restore the parent's hot item to the one with the submenu.

svn path=/branches/shell-experiments/; revision=62509
This commit is contained in:
David Quintana 2014-03-15 21:38:15 +00:00
parent 492eebcc0e
commit bacc079c22
7 changed files with 240 additions and 25 deletions

View file

@ -347,6 +347,35 @@ HRESULT STDMETHODCALLTYPE CMenuBand::ShowDW(BOOL fShow)
return hr; return hr;
} }
CComPtr<IObjectWithSite> ows;
if (SUCCEEDED(m_subMenuParent->QueryInterface(IID_PPV_ARG(IObjectWithSite, &ows))))
{
CComPtr<IServiceProvider> sp;
if (SUCCEEDED(ows->GetSite(IID_PPV_ARG(IServiceProvider, &sp))))
{
CComPtr<IDeskBar> db0;
if (SUCCEEDED(sp->QueryInterface(IID_PPV_ARG(IDeskBar, &db0))))
{
CComPtr<IUnknown> unk0;
if (SUCCEEDED(db0->GetClient(&unk0)))
{
CComPtr<IDeskBar> db;
if (SUCCEEDED(IUnknown_QueryService(unk0, SID_SMenuBandChild, IID_PPV_ARG(IDeskBar, &db))))
{
CComPtr<IDeskBar> db1;
if (SUCCEEDED(IUnknown_QueryService(m_site, SID_SMenuBandParent, IID_PPV_ARG(IDeskBar, &db1))))
{
if (fShow)
db->SetClient(db1);
else
db->SetClient(NULL);
}
}
}
}
}
}
if (m_dwFlags & SMINIT_VERTICAL) if (m_dwFlags & SMINIT_VERTICAL)
{ {
if (fShow) if (fShow)
@ -494,8 +523,11 @@ HRESULT STDMETHODCALLTYPE CMenuBand::SetSubMenu(IMenuPopup *pmp, BOOL fSet)
HRESULT STDMETHODCALLTYPE CMenuBand::SetClient(IUnknown *punkClient) HRESULT STDMETHODCALLTYPE CMenuBand::SetClient(IUnknown *punkClient)
{ {
UNIMPLEMENTED; if (m_subMenuChild)
m_subMenuChild = NULL;
if (!punkClient)
return S_OK; return S_OK;
return punkClient->QueryInterface(IID_PPV_ARG(IMenuPopup, &m_subMenuChild));
} }
HRESULT STDMETHODCALLTYPE CMenuBand::GetClient(IUnknown **ppunkClient) HRESULT STDMETHODCALLTYPE CMenuBand::GetClient(IUnknown **ppunkClient)
@ -673,6 +705,10 @@ HRESULT CMenuBand::_GetTopLevelWindow(HWND*topLevel)
HRESULT CMenuBand::_OnHotItemChanged(CMenuToolbarBase * tb, INT id) HRESULT CMenuBand::_OnHotItemChanged(CMenuToolbarBase * tb, INT id)
{ {
if (m_subMenuChild && id == -1)
{
return S_FALSE;
}
m_hotBar = tb; m_hotBar = tb;
m_hotItem = id; m_hotItem = id;
if (m_staticToolbar) m_staticToolbar->OnHotItemChanged(tb, id); if (m_staticToolbar) m_staticToolbar->OnHotItemChanged(tb, id);
@ -779,7 +815,6 @@ HRESULT CMenuBand::_OnPopupSubMenu(IMenuPopup * popup, POINTL * pAt, RECTL * pEx
} }
if (m_staticToolbar) m_staticToolbar->OnPopupItemChanged(toolbar, item); if (m_staticToolbar) m_staticToolbar->OnPopupItemChanged(toolbar, item);
if (m_SFToolbar) m_SFToolbar->OnPopupItemChanged(toolbar, item); if (m_SFToolbar) m_SFToolbar->OnPopupItemChanged(toolbar, item);
m_subMenuChild = popup;
if (popup) if (popup)
{ {
if (m_subMenuParent) if (m_subMenuParent)
@ -792,6 +827,15 @@ HRESULT CMenuBand::_OnPopupSubMenu(IMenuPopup * popup, POINTL * pAt, RECTL * pEx
return S_OK; return S_OK;
} }
HRESULT CMenuBand::_DisableMouseTrack(BOOL bDisable)
{
if (m_staticToolbar)
m_staticToolbar->DisableMouseTrack(bDisable);
if (m_SFToolbar)
m_SFToolbar->DisableMouseTrack(bDisable);
return S_OK;
}
HRESULT STDMETHODCALLTYPE CMenuBand::GetSubMenu(THIS) HRESULT STDMETHODCALLTYPE CMenuBand::GetSubMenu(THIS)
{ {
UNIMPLEMENTED; UNIMPLEMENTED;

View file

@ -176,6 +176,7 @@ public:
HRESULT _OnHotItemChanged(CMenuToolbarBase * tb, INT id); HRESULT _OnHotItemChanged(CMenuToolbarBase * tb, INT id);
HRESULT _MenuItemHotTrack(DWORD changeType); HRESULT _MenuItemHotTrack(DWORD changeType);
HRESULT _OnPopupSubMenu(IMenuPopup * popup, POINTL * pAt, RECTL * pExclude, CMenuToolbarBase * toolbar, INT item); HRESULT _OnPopupSubMenu(IMenuPopup * popup, POINTL * pAt, RECTL * pExclude, CMenuToolbarBase * toolbar, INT item);
HRESULT _DisableMouseTrack(BOOL bDisable);
BOOL UseBigIcons() BOOL UseBigIcons()
{ {

View file

@ -607,8 +607,6 @@ LRESULT CMenuDeskBar::_OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bH
LRESULT CMenuDeskBar::_OnActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) LRESULT CMenuDeskBar::_OnActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{ {
DbgPrint("BaseBar %08p (de)activated (%08x, %08x).\n", m_hWnd, wParam, lParam);
// BUG in ReactOS: WM_ACTIVATE/WA_INACTIVE makes no sense with lParam==hWnd // BUG in ReactOS: WM_ACTIVATE/WA_INACTIVE makes no sense with lParam==hWnd
if (LOWORD(wParam) != 0 || reinterpret_cast<HWND>(lParam) == m_hWnd) if (LOWORD(wParam) != 0 || reinterpret_cast<HWND>(lParam) == m_hWnd)
{ {

View file

@ -115,7 +115,10 @@ HRESULT CMenuFocusManager::PeekArray(CMenuBand ** pItem)
CMenuFocusManager::CMenuFocusManager() : CMenuFocusManager::CMenuFocusManager() :
m_currentBand(NULL), m_currentBand(NULL),
m_currentFocus(NULL), m_currentFocus(NULL),
m_bandCount(0) m_bandCount(0),
m_mouseTrackDisabled(FALSE),
m_lastMoveFlags(0),
m_lastMovePos(0)
{ {
m_threadId = GetCurrentThreadId(); m_threadId = GetCurrentThreadId();
} }
@ -124,11 +127,73 @@ CMenuFocusManager::~CMenuFocusManager()
{ {
} }
void CMenuFocusManager::DisableMouseTrack(HWND enableTo, BOOL disableThis)
{
BOOL bDisable = FALSE;
int i = m_bandCount;
while (--i >= 0)
{
CMenuBand * band = m_bandStack[i];
HWND hwnd;
HRESULT hr = band->_GetTopLevelWindow(&hwnd);
if (FAILED_UNEXPECTEDLY(hr))
break;
if (hwnd == enableTo)
{
band->_DisableMouseTrack(disableThis);
bDisable = TRUE;
}
else
{
band->_DisableMouseTrack(bDisable);
}
}
if (m_mouseTrackDisabled == bDisable)
{
if (bDisable)
{
SetCapture(m_currentFocus);
}
else
ReleaseCapture();
m_mouseTrackDisabled = bDisable;
}
}
HRESULT CMenuFocusManager::IsTrackedWindow(HWND hWnd)
{
if (hWnd == m_currentFocus)
return S_OK;
int i = m_bandCount - 1;
while (--i >= 0)
{
CMenuBand * band = m_bandStack[i];
HWND hwnd;
HRESULT hr = band->_GetTopLevelWindow(&hwnd);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
if (hwnd == hWnd)
return S_OK;
}
return S_FALSE;
}
LRESULT CMenuFocusManager::GetMsgHook(INT nCode, WPARAM wParam, LPARAM lParam) LRESULT CMenuFocusManager::GetMsgHook(INT nCode, WPARAM wParam, LPARAM lParam)
{ {
if (nCode < 0) if (nCode < 0)
return CallNextHookEx(m_hHook, nCode, wParam, lParam); return CallNextHookEx(m_hHook, nCode, wParam, lParam);
DWORD pos = GetMessagePos();
if (nCode == HC_ACTION) if (nCode == HC_ACTION)
{ {
BOOL callNext = TRUE; BOOL callNext = TRUE;
@ -138,10 +203,33 @@ LRESULT CMenuFocusManager::GetMsgHook(INT nCode, WPARAM wParam, LPARAM lParam)
switch (msg->message) switch (msg->message)
{ {
case WM_ACTIVATE: // does not trigger
ActivationChange(msg->hwnd);
case WM_CLOSE: case WM_CLOSE:
break; break;
case WM_MOUSEMOVE:
if (m_lastMoveFlags != wParam || m_lastMovePos != pos)
{
m_lastMoveFlags = wParam;
m_lastMovePos = pos;
POINT pt = { GET_X_LPARAM(pos), GET_Y_LPARAM(pos) };
HWND window = WindowFromPoint(pt);
if (IsTrackedWindow(window) == S_OK)
{
DisableMouseTrack(window, FALSE);
}
else
{
DisableMouseTrack(NULL, FALSE);
}
}
break;
case WM_SYSKEYDOWN: case WM_SYSKEYDOWN:
case WM_KEYDOWN: case WM_KEYDOWN:
DisableMouseTrack(m_currentFocus, TRUE);
switch (msg->wParam) switch (msg->wParam)
{ {
case VK_MENU: case VK_MENU:
@ -193,6 +281,36 @@ HRESULT CMenuFocusManager::RemoveHooks(HWND window)
return S_OK; return S_OK;
} }
HRESULT CMenuFocusManager::ActivationChange(HWND newHwnd)
{
HRESULT hr;
CMenuBand * newBand = NULL;
CMenuBand * band;
PeekArray(&band);
while (m_bandCount >= 0)
{
HWND hwnd;
hr = band->_GetTopLevelWindow(&hwnd);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
if (hwnd == newHwnd)
{
newBand = band;
break;
}
else
{
PopFromArray(NULL);
PeekArray(&band);
}
}
return UpdateFocus(newBand);
}
HRESULT CMenuFocusManager::UpdateFocus(CMenuBand * newBand) HRESULT CMenuFocusManager::UpdateFocus(CMenuBand * newBand)
{ {
HRESULT hr; HRESULT hr;
@ -217,6 +335,11 @@ HRESULT CMenuFocusManager::UpdateFocus(CMenuBand * newBand)
return hr; return hr;
} }
CHAR title[1024];
GetWindowTextA(newFocus, title, 1024);
DbgPrint("Focus is now at %08p, hwnd=%08x, title='%s'. m_bandCount=%d\n", newBand, newFocus, title, m_bandCount);
m_currentFocus = newFocus; m_currentFocus = newFocus;
m_currentBand = newBand; m_currentBand = newBand;
@ -239,14 +362,33 @@ HRESULT CMenuFocusManager::PopMenu(CMenuBand * mb)
CMenuBand * mbc; CMenuBand * mbc;
HRESULT hr; HRESULT hr;
hr = PopFromArray(&mbc); HWND newFocus;
hr = mb->_GetTopLevelWindow(&newFocus);
if (FAILED_UNEXPECTEDLY(hr)) if (FAILED_UNEXPECTEDLY(hr))
return hr; return hr;
if (mb != mbc) DbgPrint("Trying to pop %08p, hwnd=%08x\n", mb, newFocus);
do {
hr = PopFromArray(&mbc);
if (FAILED_UNEXPECTEDLY(hr))
{
mbc = NULL;
return hr;
}
}
while (mbc && mb != mbc);
hr = PeekArray(&mb);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
hr = UpdateFocus(mb);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
if (!mbc)
return E_FAIL; return E_FAIL;
hr = PeekArray(&mbc); return S_OK;
return UpdateFocus(mbc);
} }

View file

@ -43,6 +43,9 @@ private:
HWND m_currentFocus; HWND m_currentFocus;
HHOOK m_hHook; HHOOK m_hHook;
DWORD m_threadId; DWORD m_threadId;
BOOL m_mouseTrackDisabled;
WPARAM m_lastMoveFlags;
LPARAM m_lastMovePos;
// TODO: make dynamic // TODO: make dynamic
#define MAX_RECURSE 20 #define MAX_RECURSE 20
@ -64,10 +67,14 @@ public:
BEGIN_COM_MAP(CMenuFocusManager) BEGIN_COM_MAP(CMenuFocusManager)
END_COM_MAP() END_COM_MAP()
private:
LRESULT GetMsgHook(INT nCode, WPARAM wParam, LPARAM lParam); LRESULT GetMsgHook(INT nCode, WPARAM wParam, LPARAM lParam);
HRESULT PlaceHooks(HWND window); HRESULT PlaceHooks(HWND window);
HRESULT RemoveHooks(HWND window); HRESULT RemoveHooks(HWND window);
HRESULT UpdateFocus(CMenuBand * newBand); HRESULT UpdateFocus(CMenuBand * newBand);
HRESULT ActivationChange(HWND newHwnd);
void DisableMouseTrack(HWND enableTo, BOOL disableThis);
HRESULT IsTrackedWindow(HWND hWnd);
public: public:
HRESULT PushMenu(CMenuBand * mb); HRESULT PushMenu(CMenuBand * mb);

View file

@ -40,6 +40,12 @@ HRESULT WINAPI SHGetImageList(
#define TIMERID_HOTTRACK 1 #define TIMERID_HOTTRACK 1
#define SUBCLASS_ID_MENUBAND 1 #define SUBCLASS_ID_MENUBAND 1
HRESULT CMenuToolbarBase::DisableMouseTrack(BOOL bDisable)
{
m_disableMouseTrack = bDisable;
return S_OK;
}
HRESULT CMenuToolbarBase::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult) HRESULT CMenuToolbarBase::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
{ {
NMHDR * hdr; NMHDR * hdr;
@ -82,7 +88,7 @@ HRESULT CMenuToolbarBase::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM
return OnCommand(wParam, 0, theResult); return OnCommand(wParam, 0, theResult);
case TBN_HOTITEMCHANGE: case TBN_HOTITEMCHANGE:
return OnHotItemChange(reinterpret_cast<LPNMTBHOTITEM>(hdr)); return OnHotItemChange(reinterpret_cast<LPNMTBHOTITEM>(hdr), theResult);
case NM_RCLICK: case NM_RCLICK:
return OnContextMenu(reinterpret_cast<LPNMMOUSE>(hdr)); return OnContextMenu(reinterpret_cast<LPNMMOUSE>(hdr));
@ -184,6 +190,7 @@ CMenuToolbarBase::CMenuToolbarBase(CMenuBand *menuBand, BOOL usePager) :
m_hwnd(NULL), m_hwnd(NULL),
m_useFlatMenus(FALSE), m_useFlatMenus(FALSE),
m_SubclassOld(NULL), m_SubclassOld(NULL),
m_disableMouseTrack(FALSE),
m_menuBand(menuBand), m_menuBand(menuBand),
m_hwndToolbar(NULL), m_hwndToolbar(NULL),
m_dwMenuFlags(0), m_dwMenuFlags(0),
@ -417,18 +424,34 @@ LRESULT CMenuToolbarBase::SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
return m_SubclassOld(hWnd, uMsg, wParam, lParam); return m_SubclassOld(hWnd, uMsg, wParam, lParam);
} }
HRESULT CMenuToolbarBase::OnHotItemChange(const NMTBHOTITEM * hot) HRESULT CMenuToolbarBase::OnHotItemChange(const NMTBHOTITEM * hot, LRESULT * theResult)
{ {
if (m_disableMouseTrack && hot->dwFlags & HICF_MOUSE)
{
*theResult = 1;
return S_OK;
}
if (hot->dwFlags & HICF_LEAVING) if (hot->dwFlags & HICF_LEAVING)
{ {
KillTimer(m_hwndToolbar, TIMERID_HOTTRACK); KillTimer(m_hwndToolbar, TIMERID_HOTTRACK);
if (m_menuBand->_OnHotItemChanged(NULL, -1) == S_FALSE)
{
*theResult = 1;
return S_OK;
}
else
{
m_hotItem = -1; m_hotItem = -1;
m_menuBand->_OnHotItemChanged(NULL, -1);
m_menuBand->_MenuItemHotTrack(MPOS_CHILDTRACKING); m_menuBand->_MenuItemHotTrack(MPOS_CHILDTRACKING);
return S_OK;
}
} }
else if (m_hotItem != hot->idNew) else if (m_hotItem != hot->idNew)
{ {
if (m_toolbarFlags & SMINIT_VERTICAL) if (hot->dwFlags & HICF_MOUSE &&
m_toolbarFlags & SMINIT_VERTICAL)
{ {
DWORD elapsed = 0; DWORD elapsed = 0;
SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &elapsed, 0); SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &elapsed, 0);
@ -438,6 +461,7 @@ HRESULT CMenuToolbarBase::OnHotItemChange(const NMTBHOTITEM * hot)
m_hotItem = hot->idNew; m_hotItem = hot->idNew;
m_menuBand->_OnHotItemChanged(this, m_hotItem); m_menuBand->_OnHotItemChanged(this, m_hotItem);
m_menuBand->_MenuItemHotTrack(MPOS_CHILDTRACKING); m_menuBand->_MenuItemHotTrack(MPOS_CHILDTRACKING);
return S_OK;
} }
return S_OK; return S_OK;
} }
@ -693,11 +717,9 @@ HRESULT CMenuToolbarBase::ChangeHotItem(DWORD dwSelectType)
if (btn.dwData) if (btn.dwData)
{ {
m_hotItem = btn.idCommand; if (prev != btn.idCommand)
if (prev != m_hotItem)
{ {
SendMessage(m_hwndToolbar, TB_SETHOTITEM, index, 0); SendMessage(m_hwndToolbar, TB_SETHOTITEM, index, 0);
return m_menuBand->_OnHotItemChanged(this, m_hotItem);
} }
return S_OK; return S_OK;
} }
@ -713,11 +735,9 @@ HRESULT CMenuToolbarBase::ChangeHotItem(DWORD dwSelectType)
} }
} }
m_hotItem = -1; if (prev != -1)
if (prev != m_hotItem)
{ {
SendMessage(m_hwndToolbar, TB_SETHOTITEM, -1, 0); SendMessage(m_hwndToolbar, TB_SETHOTITEM, -1, 0);
m_menuBand->_OnHotItemChanged(NULL, -1);
} }
return S_FALSE; return S_FALSE;
} }

View file

@ -29,6 +29,7 @@ private:
HFONT m_marlett; HFONT m_marlett;
BOOL m_useFlatMenus; BOOL m_useFlatMenus;
WNDPROC m_SubclassOld; WNDPROC m_SubclassOld;
BOOL m_disableMouseTrack;
protected: protected:
CMenuBand * m_menuBand; CMenuBand * m_menuBand;
@ -67,13 +68,15 @@ public:
HRESULT DoContextMenu(IContextMenu* contextMenu); HRESULT DoContextMenu(IContextMenu* contextMenu);
HRESULT ChangeHotItem(DWORD changeType); HRESULT ChangeHotItem(DWORD changeType);
HRESULT OnHotItemChange(const NMTBHOTITEM * hot); HRESULT OnHotItemChange(const NMTBHOTITEM * hot, LRESULT * theResult);
HRESULT GetIdealSize(SIZE& size); HRESULT GetIdealSize(SIZE& size);
HRESULT SetPosSize(int x, int y, int cx, int cy); HRESULT SetPosSize(int x, int y, int cx, int cy);
void InvalidateDraw(); void InvalidateDraw();
HRESULT DisableMouseTrack(BOOL bDisable);
virtual HRESULT FillToolbar(BOOL clearFirst=FALSE) = 0; virtual HRESULT FillToolbar(BOOL clearFirst=FALSE) = 0;
virtual HRESULT OnContextMenu(NMMOUSE * rclick) = 0; virtual HRESULT OnContextMenu(NMMOUSE * rclick) = 0;