From bacc079c22283941519fc5c294179e9e9f447bb1 Mon Sep 17 00:00:00 2001 From: David Quintana Date: Sat, 15 Mar 2014 21:38:15 +0000 Subject: [PATCH] [RSHELL] * 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 --- base/shell/rshell/CMenuBand.cpp | 50 +++++++- base/shell/rshell/CMenuBand.h | 1 + base/shell/rshell/CMenuDeskBar.cpp | 2 - base/shell/rshell/CMenuFocusManager.cpp | 154 +++++++++++++++++++++++- base/shell/rshell/CMenuFocusManager.h | 7 ++ base/shell/rshell/CMenuToolbars.cpp | 46 +++++-- base/shell/rshell/CMenuToolbars.h | 5 +- 7 files changed, 240 insertions(+), 25 deletions(-) diff --git a/base/shell/rshell/CMenuBand.cpp b/base/shell/rshell/CMenuBand.cpp index 3da636970f0..da5d5c124cd 100644 --- a/base/shell/rshell/CMenuBand.cpp +++ b/base/shell/rshell/CMenuBand.cpp @@ -347,6 +347,35 @@ HRESULT STDMETHODCALLTYPE CMenuBand::ShowDW(BOOL fShow) return hr; } + CComPtr ows; + if (SUCCEEDED(m_subMenuParent->QueryInterface(IID_PPV_ARG(IObjectWithSite, &ows)))) + { + CComPtr sp; + if (SUCCEEDED(ows->GetSite(IID_PPV_ARG(IServiceProvider, &sp)))) + { + CComPtr db0; + if (SUCCEEDED(sp->QueryInterface(IID_PPV_ARG(IDeskBar, &db0)))) + { + CComPtr unk0; + if (SUCCEEDED(db0->GetClient(&unk0))) + { + CComPtr db; + if (SUCCEEDED(IUnknown_QueryService(unk0, SID_SMenuBandChild, IID_PPV_ARG(IDeskBar, &db)))) + { + CComPtr 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 (fShow) @@ -494,8 +523,11 @@ HRESULT STDMETHODCALLTYPE CMenuBand::SetSubMenu(IMenuPopup *pmp, BOOL fSet) HRESULT STDMETHODCALLTYPE CMenuBand::SetClient(IUnknown *punkClient) { - UNIMPLEMENTED; - return S_OK; + if (m_subMenuChild) + m_subMenuChild = NULL; + if (!punkClient) + return S_OK; + return punkClient->QueryInterface(IID_PPV_ARG(IMenuPopup, &m_subMenuChild)); } HRESULT STDMETHODCALLTYPE CMenuBand::GetClient(IUnknown **ppunkClient) @@ -673,6 +705,10 @@ HRESULT CMenuBand::_GetTopLevelWindow(HWND*topLevel) HRESULT CMenuBand::_OnHotItemChanged(CMenuToolbarBase * tb, INT id) { + if (m_subMenuChild && id == -1) + { + return S_FALSE; + } m_hotBar = tb; m_hotItem = 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_SFToolbar) m_SFToolbar->OnPopupItemChanged(toolbar, item); - m_subMenuChild = popup; if (popup) { if (m_subMenuParent) @@ -792,6 +827,15 @@ HRESULT CMenuBand::_OnPopupSubMenu(IMenuPopup * popup, POINTL * pAt, RECTL * pEx 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) { UNIMPLEMENTED; diff --git a/base/shell/rshell/CMenuBand.h b/base/shell/rshell/CMenuBand.h index b487d6e711a..e8189e57597 100644 --- a/base/shell/rshell/CMenuBand.h +++ b/base/shell/rshell/CMenuBand.h @@ -176,6 +176,7 @@ public: HRESULT _OnHotItemChanged(CMenuToolbarBase * tb, INT id); HRESULT _MenuItemHotTrack(DWORD changeType); HRESULT _OnPopupSubMenu(IMenuPopup * popup, POINTL * pAt, RECTL * pExclude, CMenuToolbarBase * toolbar, INT item); + HRESULT _DisableMouseTrack(BOOL bDisable); BOOL UseBigIcons() { diff --git a/base/shell/rshell/CMenuDeskBar.cpp b/base/shell/rshell/CMenuDeskBar.cpp index 76abbcb2502..2923fa33297 100644 --- a/base/shell/rshell/CMenuDeskBar.cpp +++ b/base/shell/rshell/CMenuDeskBar.cpp @@ -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) { - 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 if (LOWORD(wParam) != 0 || reinterpret_cast(lParam) == m_hWnd) { diff --git a/base/shell/rshell/CMenuFocusManager.cpp b/base/shell/rshell/CMenuFocusManager.cpp index ced842a57d5..6cab574cf21 100644 --- a/base/shell/rshell/CMenuFocusManager.cpp +++ b/base/shell/rshell/CMenuFocusManager.cpp @@ -115,7 +115,10 @@ HRESULT CMenuFocusManager::PeekArray(CMenuBand ** pItem) CMenuFocusManager::CMenuFocusManager() : m_currentBand(NULL), m_currentFocus(NULL), - m_bandCount(0) + m_bandCount(0), + m_mouseTrackDisabled(FALSE), + m_lastMoveFlags(0), + m_lastMovePos(0) { 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) { if (nCode < 0) return CallNextHookEx(m_hHook, nCode, wParam, lParam); + DWORD pos = GetMessagePos(); + if (nCode == HC_ACTION) { BOOL callNext = TRUE; @@ -138,10 +203,33 @@ LRESULT CMenuFocusManager::GetMsgHook(INT nCode, WPARAM wParam, LPARAM lParam) switch (msg->message) { + case WM_ACTIVATE: // does not trigger + ActivationChange(msg->hwnd); case WM_CLOSE: 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_KEYDOWN: + DisableMouseTrack(m_currentFocus, TRUE); switch (msg->wParam) { case VK_MENU: @@ -193,6 +281,36 @@ HRESULT CMenuFocusManager::RemoveHooks(HWND window) 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 hr; @@ -217,6 +335,11 @@ HRESULT CMenuFocusManager::UpdateFocus(CMenuBand * newBand) 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_currentBand = newBand; @@ -239,14 +362,33 @@ HRESULT CMenuFocusManager::PopMenu(CMenuBand * mb) CMenuBand * mbc; HRESULT hr; - hr = PopFromArray(&mbc); + HWND newFocus; + hr = mb->_GetTopLevelWindow(&newFocus); if (FAILED_UNEXPECTEDLY(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; - hr = PeekArray(&mbc); - - return UpdateFocus(mbc); + return S_OK; } diff --git a/base/shell/rshell/CMenuFocusManager.h b/base/shell/rshell/CMenuFocusManager.h index fd848880aba..66548986270 100644 --- a/base/shell/rshell/CMenuFocusManager.h +++ b/base/shell/rshell/CMenuFocusManager.h @@ -43,6 +43,9 @@ private: HWND m_currentFocus; HHOOK m_hHook; DWORD m_threadId; + BOOL m_mouseTrackDisabled; + WPARAM m_lastMoveFlags; + LPARAM m_lastMovePos; // TODO: make dynamic #define MAX_RECURSE 20 @@ -64,10 +67,14 @@ public: BEGIN_COM_MAP(CMenuFocusManager) END_COM_MAP() +private: LRESULT GetMsgHook(INT nCode, WPARAM wParam, LPARAM lParam); HRESULT PlaceHooks(HWND window); HRESULT RemoveHooks(HWND window); HRESULT UpdateFocus(CMenuBand * newBand); + HRESULT ActivationChange(HWND newHwnd); + void DisableMouseTrack(HWND enableTo, BOOL disableThis); + HRESULT IsTrackedWindow(HWND hWnd); public: HRESULT PushMenu(CMenuBand * mb); diff --git a/base/shell/rshell/CMenuToolbars.cpp b/base/shell/rshell/CMenuToolbars.cpp index 0fb1e5e07ff..1a5af137d28 100644 --- a/base/shell/rshell/CMenuToolbars.cpp +++ b/base/shell/rshell/CMenuToolbars.cpp @@ -40,6 +40,12 @@ HRESULT WINAPI SHGetImageList( #define TIMERID_HOTTRACK 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) { NMHDR * hdr; @@ -82,7 +88,7 @@ HRESULT CMenuToolbarBase::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM return OnCommand(wParam, 0, theResult); case TBN_HOTITEMCHANGE: - return OnHotItemChange(reinterpret_cast(hdr)); + return OnHotItemChange(reinterpret_cast(hdr), theResult); case NM_RCLICK: return OnContextMenu(reinterpret_cast(hdr)); @@ -183,7 +189,8 @@ HRESULT CMenuToolbarBase::OnCustomDraw(LPNMTBCUSTOMDRAW cdraw, LRESULT * theResu CMenuToolbarBase::CMenuToolbarBase(CMenuBand *menuBand, BOOL usePager) : m_hwnd(NULL), m_useFlatMenus(FALSE), - m_SubclassOld(NULL), + m_SubclassOld(NULL), + m_disableMouseTrack(FALSE), m_menuBand(menuBand), m_hwndToolbar(NULL), m_dwMenuFlags(0), @@ -417,18 +424,34 @@ LRESULT CMenuToolbarBase::SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR 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) { KillTimer(m_hwndToolbar, TIMERID_HOTTRACK); - m_hotItem = -1; - m_menuBand->_OnHotItemChanged(NULL, -1); - m_menuBand->_MenuItemHotTrack(MPOS_CHILDTRACKING); + + if (m_menuBand->_OnHotItemChanged(NULL, -1) == S_FALSE) + { + *theResult = 1; + return S_OK; + } + else + { + m_hotItem = -1; + m_menuBand->_MenuItemHotTrack(MPOS_CHILDTRACKING); + return S_OK; + } } else if (m_hotItem != hot->idNew) { - if (m_toolbarFlags & SMINIT_VERTICAL) + if (hot->dwFlags & HICF_MOUSE && + m_toolbarFlags & SMINIT_VERTICAL) { DWORD elapsed = 0; SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &elapsed, 0); @@ -438,6 +461,7 @@ HRESULT CMenuToolbarBase::OnHotItemChange(const NMTBHOTITEM * hot) m_hotItem = hot->idNew; m_menuBand->_OnHotItemChanged(this, m_hotItem); m_menuBand->_MenuItemHotTrack(MPOS_CHILDTRACKING); + return S_OK; } return S_OK; } @@ -693,11 +717,9 @@ HRESULT CMenuToolbarBase::ChangeHotItem(DWORD dwSelectType) if (btn.dwData) { - m_hotItem = btn.idCommand; - if (prev != m_hotItem) + if (prev != btn.idCommand) { SendMessage(m_hwndToolbar, TB_SETHOTITEM, index, 0); - return m_menuBand->_OnHotItemChanged(this, m_hotItem); } return S_OK; } @@ -713,11 +735,9 @@ HRESULT CMenuToolbarBase::ChangeHotItem(DWORD dwSelectType) } } - m_hotItem = -1; - if (prev != m_hotItem) + if (prev != -1) { SendMessage(m_hwndToolbar, TB_SETHOTITEM, -1, 0); - m_menuBand->_OnHotItemChanged(NULL, -1); } return S_FALSE; } diff --git a/base/shell/rshell/CMenuToolbars.h b/base/shell/rshell/CMenuToolbars.h index 9195b8eafd5..a6118138606 100644 --- a/base/shell/rshell/CMenuToolbars.h +++ b/base/shell/rshell/CMenuToolbars.h @@ -29,6 +29,7 @@ private: HFONT m_marlett; BOOL m_useFlatMenus; WNDPROC m_SubclassOld; + BOOL m_disableMouseTrack; protected: CMenuBand * m_menuBand; @@ -67,13 +68,15 @@ public: HRESULT DoContextMenu(IContextMenu* contextMenu); HRESULT ChangeHotItem(DWORD changeType); - HRESULT OnHotItemChange(const NMTBHOTITEM * hot); + HRESULT OnHotItemChange(const NMTBHOTITEM * hot, LRESULT * theResult); HRESULT GetIdealSize(SIZE& size); HRESULT SetPosSize(int x, int y, int cx, int cy); void InvalidateDraw(); + HRESULT DisableMouseTrack(BOOL bDisable); + virtual HRESULT FillToolbar(BOOL clearFirst=FALSE) = 0; virtual HRESULT OnContextMenu(NMMOUSE * rclick) = 0;