* CMenuBand, CMenuDeskBar: Improve the SubMenu hierarchy and OnSelect flow. Now the menu will spread a FullCancel or Execute upwards and close the whole start menu.
CORE-7881

svn path=/branches/shell-experiments/; revision=62280
This commit is contained in:
David Quintana 2014-02-21 10:00:00 +00:00
parent 9f9be3f9bb
commit 07467c9e06
3 changed files with 155 additions and 93 deletions

View file

@ -140,7 +140,8 @@ private:
CComPtr<IOleWindow> m_site;
CComPtr<IShellMenuCallback> m_psmc;
CComPtr<IMenuPopup> m_childMenu;
CComPtr<IMenuPopup> m_subMenuChild;
CComPtr<IMenuPopup> m_subMenuParent;
UINT m_uId;
UINT m_uIdAncestor;
@ -269,7 +270,7 @@ public:
HRESULT _GetTopLevelWindow(HWND*topLevel);
HRESULT _OnHotItemChanged(CMenuToolbarBase * tb, INT id);
HRESULT _MenuItemHotTrack(DWORD changeType);
HRESULT _OnPopupSubMenu(IMenuPopup * popup);
HRESULT _OnPopupSubMenu(IMenuPopup * popup, POINTL * pAt, RECTL * pExclude);
BOOL UseBigIcons()
{
@ -332,7 +333,7 @@ private:
private:
// TODO: make dynamic
#define MAX_RECURSE 100
#define MAX_RECURSE 20
CMenuBand* m_bandStack[MAX_RECURSE];
int m_bandCount;
@ -358,6 +359,8 @@ private:
if (pItem)
*pItem = m_bandStack[m_bandCount];
m_bandStack[m_bandCount] = NULL;
return S_OK;
}
@ -676,7 +679,7 @@ LRESULT CMenuToolbarBase::SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
case WM_TIMER:
if (wParam == TIMERID_HOTTRACK)
{
m_menuBand->_OnPopupSubMenu(NULL);
m_menuBand->_OnPopupSubMenu(NULL, NULL, NULL);
PopupItem(m_hotItem);
KillTimer(hWnd, TIMERID_HOTTRACK);
}
@ -776,9 +779,7 @@ HRESULT CMenuToolbarBase::PopupSubMenu(UINT index, IShellMenu* childShellMenu)
if (FAILED(hr))
return hr;
popup->Popup(&pt, &rcl, MPPF_TOP | MPPF_RIGHT);
m_menuBand->_OnPopupSubMenu(popup);
m_menuBand->_OnPopupSubMenu(popup, &pt, &rcl);
return S_OK;
}
@ -1037,11 +1038,12 @@ HRESULT CMenuStaticToolbar::OnContextMenu(NMMOUSE * rclick)
HRESULT CMenuStaticToolbar::OnCommand(WPARAM wParam, LPARAM lParam, LRESULT *theResult)
{
HRESULT hr = m_menuBand->_CallCBWithItemId(wParam, SMC_EXEC, 0, 0);
HRESULT hr;
hr = CMenuToolbarBase::OnCommand(wParam, lParam, theResult);
if (FAILED(hr))
return hr;
return CMenuToolbarBase::OnCommand(wParam, lParam, theResult);
return m_menuBand->_CallCBWithItemId(wParam, SMC_EXEC, 0, 0);
}
HRESULT CMenuStaticToolbar::PopupItem(UINT uItem)
@ -1099,13 +1101,9 @@ CMenuSFToolbar::~CMenuSFToolbar()
HRESULT CMenuSFToolbar::FillToolbar()
{
HRESULT hr;
TBBUTTON tbb = { 0 };
int i = 0;
PWSTR MenuString;
tbb.fsState = TBSTATE_ENABLED;
tbb.fsStyle = 0;
IEnumIDList * eidl;
m_shellFolder->EnumObjects(m_hwnd, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &eidl);
@ -1116,6 +1114,10 @@ HRESULT CMenuSFToolbar::FillToolbar()
INT index = 0;
INT indexOpen = 0;
TBBUTTON tbb = { 0 };
tbb.fsState = TBSTATE_ENABLED;
tbb.fsStyle = 0;
CComPtr<IShellItem> psi;
SHCreateShellItem(NULL, m_shellFolder, item, &psi);
@ -1222,9 +1224,12 @@ HRESULT CMenuSFToolbar::OnContextMenu(NMMOUSE * rclick)
HRESULT CMenuSFToolbar::OnCommand(WPARAM wParam, LPARAM lParam, LRESULT *theResult)
{
HRESULT hr = m_menuBand->_CallCBWithItemPidl(GetPidlFromId(wParam), SMC_SFEXEC, 0, 0);
HRESULT hr;
hr = CMenuToolbarBase::OnCommand(wParam, lParam, theResult);
if (FAILED(hr))
return hr;
return CMenuToolbarBase::OnCommand(wParam, lParam, theResult);
return m_menuBand->_CallCBWithItemPidl(GetPidlFromId(wParam), SMC_SFEXEC, 0, 0);
}
HRESULT CMenuSFToolbar::PopupItem(UINT uItem)
@ -1299,7 +1304,7 @@ CMenuBand::CMenuBand() :
m_useBigIcons(FALSE),
m_hotBar(NULL),
m_hotItem(-1),
m_childMenu(NULL)
m_subMenuChild(NULL)
{
m_focusManager = CMenuFocusManager::AcquireManager();
}
@ -1321,7 +1326,8 @@ HRESULT STDMETHODCALLTYPE CMenuBand::Initialize(
UINT uIdAncestor,
DWORD dwFlags)
{
m_psmc = psmc;
if (m_psmc != psmc)
m_psmc = psmc;
m_uId = uId;
m_uIdAncestor = uIdAncestor;
m_dwFlags = dwFlags;
@ -1446,6 +1452,10 @@ HRESULT STDMETHODCALLTYPE CMenuBand::SetSite(IUnknown *pUnkSite)
return hr;
}
hr = IUnknown_QueryService(m_site, SID_SMenuPopup, IID_PPV_ARG(IMenuPopup, &m_subMenuParent));
if (FAILED(hr))
return hr;
CComPtr<IOleWindow> pTopLevelWindow;
hr = IUnknown_QueryService(m_site, SID_STopLevelBrowser, IID_PPV_ARG(IOleWindow, &pTopLevelWindow));
if (FAILED(hr))
@ -1498,7 +1508,10 @@ HRESULT STDMETHODCALLTYPE CMenuBand::OnPosRectChangeDB(RECT *prc)
if (hwndStatic) SendMessageW(hwndStatic, TB_GETIDEALSIZE, TRUE, reinterpret_cast<LPARAM>(&sizeStaticY));
if (hwndShlFld) SendMessageW(hwndShlFld, TB_GETIDEALSIZE, TRUE, reinterpret_cast<LPARAM>(&sizeShlFldY));
int sy = max(prc->bottom - prc->top, sizeStaticY.cy + sizeShlFldY.cy);
int sy = min(prc->bottom - prc->top, sizeStaticY.cy + sizeShlFldY.cy);
int syStatic = sizeStaticY.cy;
int syShlFld = sy - syStatic;
if (hwndShlFld)
{
@ -1506,7 +1519,7 @@ HRESULT STDMETHODCALLTYPE CMenuBand::OnPosRectChangeDB(RECT *prc)
prc->left,
prc->top,
prc->right - prc->left,
sizeShlFldY.cy,
syShlFld,
0);
DWORD btnSize = SendMessage(hwndShlFld, TB_GETBUTTONSIZE, 0, 0);
SendMessage(hwndShlFld, TB_SETBUTTONSIZE, 0, MAKELPARAM(prc->right - prc->left, HIWORD(btnSize)));
@ -1515,9 +1528,9 @@ HRESULT STDMETHODCALLTYPE CMenuBand::OnPosRectChangeDB(RECT *prc)
{
SetWindowPos(hwndStatic, hwndShlFld,
prc->left,
prc->top + sizeShlFldY.cy,
prc->top + syShlFld,
prc->right - prc->left,
sy - sizeShlFldY.cy,
syStatic,
0);
DWORD btnSize = SendMessage(hwndStatic, TB_GETBUTTONSIZE, 0, 0);
SendMessage(hwndStatic, TB_SETBUTTONSIZE, 0, MAKELPARAM(prc->right - prc->left, HIWORD(btnSize)));
@ -1573,6 +1586,9 @@ HRESULT STDMETHODCALLTYPE CMenuBand::GetBandInfo(
if (hwndStatic) SendMessageW(hwndStatic, TB_GETMAXSIZE, 0, reinterpret_cast<LPARAM>(&sizeStatic));
if (hwndShlFld) SendMessageW(hwndShlFld, TB_GETMAXSIZE, 0, reinterpret_cast<LPARAM>(&sizeShlFld));
sizeStatic.cx += 64;
sizeShlFld.cx += 64;
pdbi->ptMaxSize.x = max(sizeStatic.cx, sizeShlFld.cx); // ignored
pdbi->ptMaxSize.y = sizeStatic.cy + sizeShlFld.cy;
}
@ -1629,7 +1645,7 @@ HRESULT STDMETHODCALLTYPE CMenuBand::ShowDW(BOOL fShow)
else
hr = m_focusManager->PopMenu(this);
return hr;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CMenuBand::CloseDW(DWORD dwReserved)
@ -1659,13 +1675,8 @@ HRESULT STDMETHODCALLTYPE CMenuBand::ContextSensitiveHelp(BOOL fEnterMode)
HRESULT STDMETHODCALLTYPE CMenuBand::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
{
HRESULT hr;
CComPtr<IMenuPopup> pmp;
hr = IUnknown_QueryService(m_site, SID_SMenuPopup, IID_PPV_ARG(IMenuPopup, &pmp));
if (FAILED(hr))
return hr;
hr = pmp->SetSubMenu(this, fActivate);
hr = m_subMenuParent->SetSubMenu(this, fActivate);
if (FAILED(hr))
return hr;
@ -1776,17 +1787,30 @@ HRESULT STDMETHODCALLTYPE CMenuBand::Popup(POINTL *ppt, RECTL *prcExclude, MP_PO
HRESULT STDMETHODCALLTYPE CMenuBand::OnSelect(DWORD dwSelectType)
{
if (dwSelectType != MPOS_CANCELLEVEL)
switch (dwSelectType)
{
if (dwSelectType == MPOS_SELECTLEFT)
case MPOS_CHILDTRACKING:
// TODO: Cancel timers?
return m_subMenuParent->OnSelect(dwSelectType);
case MPOS_SELECTLEFT:
if (m_subMenuChild)
m_subMenuChild->OnSelect(MPOS_CANCELLEVEL);
return m_subMenuParent->OnSelect(dwSelectType);
case MPOS_SELECTRIGHT:
if (m_hotBar && m_hotItem >= 0)
{
dwSelectType = MPOS_CANCELLEVEL;
// TODO: popup the current child if it has subitems, otherwise spread up.
}
CComPtr<IMenuPopup> pmp;
HRESULT hr = IUnknown_QueryService(m_site, SID_SMenuPopup, IID_PPV_ARG(IMenuPopup, &pmp));
if (FAILED(hr))
return hr;
return pmp->OnSelect(dwSelectType);
return m_subMenuParent->OnSelect(dwSelectType);
case MPOS_EXECUTE:
case MPOS_FULLCANCEL:
if (m_subMenuChild)
m_subMenuChild->OnSelect(dwSelectType);
return m_subMenuParent->OnSelect(dwSelectType);
case MPOS_CANCELLEVEL:
if (m_subMenuChild)
m_subMenuChild->OnSelect(dwSelectType);
break;
}
return S_FALSE;
}
@ -2180,28 +2204,25 @@ HRESULT CMenuBand::_MenuItemHotTrack(DWORD changeType)
}
else
{
CComPtr<IMenuPopup> pmp;
hr = IUnknown_QueryService(m_site, SID_SMenuPopup, IID_PPV_ARG(IMenuPopup, &pmp));
if (FAILED(hr))
return hr;
pmp->OnSelect(changeType);
m_subMenuParent->OnSelect(changeType);
}
return S_OK;
}
HRESULT CMenuBand::_OnPopupSubMenu(IMenuPopup * popup)
HRESULT CMenuBand::_OnPopupSubMenu(IMenuPopup * popup, POINTL * pAt, RECTL * pExclude)
{
if (m_childMenu)
if (m_subMenuChild)
{
HRESULT hr = m_childMenu->OnSelect(MPOS_CANCELLEVEL);
HRESULT hr = m_subMenuChild->OnSelect(MPOS_CANCELLEVEL);
if (FAILED(hr))
return hr;
}
m_childMenu = popup;
if (m_childMenu)
m_subMenuChild = popup;
if (popup)
{
return m_childMenu->SetSubMenu(this, TRUE);
IUnknown_SetSite(popup, m_subMenuParent);
popup->Popup(pAt, pExclude, MPPF_TOP | MPPF_RIGHT);
}
return S_OK;
}
}

View file

@ -47,12 +47,15 @@ private:
CComPtr<IUnknown> m_Site;
CComPtr<IUnknown> m_Client;
CComPtr<IMenuPopup> m_SubMenuParent;
CComPtr<IMenuPopup> m_SubMenuChild;
HWND m_ClientWindow;
DWORD m_IconSize;
HBITMAP m_Banner;
INT m_Level;
public:
CMenuDeskBar();
~CMenuDeskBar();
@ -130,6 +133,8 @@ private:
LRESULT _OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT _OnWindowPosChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT _OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
HRESULT _CloseBar();
};
extern "C"
@ -150,14 +155,18 @@ HRESULT CMenuDeskBar_Constructor(REFIID riid, LPVOID *ppv)
return hr;
}
INT deskBarCount=0;
CMenuDeskBar::CMenuDeskBar() :
m_Client(NULL),
m_Banner(NULL)
m_Banner(NULL),
m_Level(deskBarCount++)
{
}
CMenuDeskBar::~CMenuDeskBar()
{
deskBarCount--;
}
HRESULT STDMETHODCALLTYPE CMenuDeskBar::GetWindow(HWND *lphwnd)
@ -316,8 +325,12 @@ HRESULT STDMETHODCALLTYPE CMenuDeskBar::OnPosRectChangeDB(LPRECT prc)
HRESULT STDMETHODCALLTYPE CMenuDeskBar::SetSite(IUnknown *pUnkSite)
{
// Windows closes the bar if this is called when the bar is shown
m_Site = pUnkSite;
IUnknown_QueryService(m_Site, SID_SMenuPopup, IID_PPV_ARG(IMenuPopup, &m_SubMenuParent));
return S_OK;
}
@ -468,12 +481,24 @@ HRESULT STDMETHODCALLTYPE CMenuDeskBar::Popup(POINTL *ppt, RECTL *prcExclude, MP
int cx = rc.right;
int cy = rc.bottom;
if (y < 0)
RECT rcWorkArea;
SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, 0);
int waHeight = rcWorkArea.bottom - rcWorkArea.top;
if (y < rcWorkArea.top)
{
y = 0;
y = rcWorkArea.top;
}
// if (y+cy > work area height) cy = work area height - y
if (cy > waHeight)
{
cy = waHeight;
}
else if (y + cy > rcWorkArea.bottom)
{
y = rcWorkArea.bottom - cy;
}
this->SetWindowPos(HWND_TOPMOST, x, y, cx, cy, SWP_SHOWWINDOW);
@ -533,70 +558,87 @@ HRESULT STDMETHODCALLTYPE CMenuDeskBar::GetBitmap(THIS_ HBITMAP* phBitmap)
HRESULT STDMETHODCALLTYPE CMenuDeskBar::OnSelect(
DWORD dwSelectType)
{
CComPtr<IMenuPopup> pmp;
CComPtr<IDeskBarClient> dbc;
HRESULT hr;
/* As far as I can tell, the submenu hierarchy looks like this:
The DeskBar's Child is the Band it contains.
The DeskBar's Parent is the SID_SMenuPopup of the Site.
The Band's Child is the IMenuPopup of the child submenu.
The Band's Parent is the SID_SMenuPopup of the Site (the DeskBar).
When the DeskBar receives a selection event:
If it requires closing the window, it will notify the Child (Band) using CancelLevel.
If it has to spread upwards (everything but CancelLevel), it will notify the Parent.
When the Band receives a selection event, this is where it gets fuzzy:
In which cases does it call the Parent? Probably not CancelLevel.
In which cases does it call the Child?
How does it react to calls?
*/
switch (dwSelectType)
{
case MPOS_EXECUTE:
case MPOS_FULLCANCEL:
case MPOS_CANCELLEVEL:
hr = IUnknown_QueryService(m_Client, SID_SMenuBandChild, IID_PPV_ARG(IMenuPopup, &pmp));
if (FAILED(hr))
return hr;
hr = pmp->OnSelect(MPOS_CANCELLEVEL);
if (FAILED(hr))
return hr;
hr = m_Client->QueryInterface(IID_PPV_ARG(IDeskBarClient, &dbc));
if (FAILED(hr))
return hr;
hr = dbc->UIActivateDBC(FALSE);
if (FAILED(hr))
return hr;
SetWindowPos(m_hWnd, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
UIActivateIO(FALSE, NULL);
_CloseBar();
if (dwSelectType == MPOS_CANCELLEVEL)
break;
return S_OK;
case MPOS_SELECTLEFT:
case MPOS_SELECTRIGHT:
/*CComPtr<IMenuPopup> pmp;
hr = IUnknown_QueryService(m_Client, SID_SMenuBandChild, IID_PPV_ARG(IMenuPopup, &pmp));
if (FAILED(hr))
return hr;*/
hr = m_SubMenuParent->OnSelect(dwSelectType);
if (FAILED(hr))
return hr;
case MPOS_CHILDTRACKING:
if (m_SubMenuParent)
return m_SubMenuParent->OnSelect(dwSelectType);
break;
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE CMenuDeskBar::SetSubMenu(
IMenuPopup *pmp,
BOOL fSet)
HRESULT CMenuDeskBar::_CloseBar()
{
CComPtr<IDeskBarClient> dbc;
HRESULT hr;
if (m_SubMenuChild)
{
hr = m_SubMenuChild->OnSelect(MPOS_CANCELLEVEL);
if (FAILED(hr))
return hr;
}
hr = m_Client->QueryInterface(IID_PPV_ARG(IDeskBarClient, &dbc));
if (FAILED(hr))
return hr;
hr = dbc->UIActivateDBC(FALSE);
if (FAILED(hr))
return hr;
SetWindowPos(m_hWnd, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
return UIActivateIO(FALSE, NULL);
}
HRESULT STDMETHODCALLTYPE CMenuDeskBar::SetSubMenu(IMenuPopup *pmp, BOOL fSet)
{
// Called by the CHILD to notify the parent of the submenu object
if (fSet)
{
m_SubMenuParent = pmp;
m_SubMenuChild = pmp;
}
else
{
if (m_SubMenuParent)
if (m_SubMenuChild)
{
if (SHIsSameObject(pmp, m_SubMenuParent))
if (SHIsSameObject(pmp, m_SubMenuChild))
{
m_SubMenuParent = NULL;
m_SubMenuChild = NULL;
}
}
}

View file

@ -180,7 +180,7 @@ HRESULT STDMETHODCALLTYPE CMenuSiteWrap::EnumBands(UINT uBand, DWORD* pdwBandID)
HRESULT STDMETHODCALLTYPE CMenuSiteWrap::QueryBand(DWORD dwBandID, IDeskBand **ppstb, DWORD *pdwState, LPWSTR pszName, int cchName)
{
WrapLogEnter("CMenuSiteWrap<%p>::QueryBand(DWORD dwBandID=%d, IDeskBand **ppstb=%p, DWORD *pdwState=%p, LPWSTR pszName=%p, int cchName=%p)\n", this, dwBandID, ppstb, pdwState, pszName, cchName);
WrapLogEnter("CMenuSiteWrap<%p>::QueryBand(DWORD dwBandID=%d, IDeskBand **ppstb=%p, DWORD *pdwState=%p, LPWSTR pszName=%p, int cchName=%d)\n", this, dwBandID, ppstb, pdwState, pszName, cchName);
HRESULT hr = m_IBandSite->QueryBand(dwBandID, ppstb, pdwState, pszName, cchName);
if (ppstb) WrapLogPost("*ppstb=%p\n", *ppstb);
if (pdwState) WrapLogPost("*pdwState=%d\n", *pdwState);
@ -190,7 +190,7 @@ HRESULT STDMETHODCALLTYPE CMenuSiteWrap::QueryBand(DWORD dwBandID, IDeskBand **p
HRESULT STDMETHODCALLTYPE CMenuSiteWrap::GetBandObject(DWORD dwBandID, REFIID riid, VOID **ppv)
{
WrapLogEnter("CMenuSiteWrap<%p>::GetBandObject(DWORD dwBandID, REFIID riid, VOID **ppv)\n", this, dwBandID, riid, ppv);
WrapLogEnter("CMenuSiteWrap<%p>::GetBandObject(DWORD dwBandID=%d, REFIID riid=%s, VOID **ppv=%p)\n", this, dwBandID, Wrap(riid), ppv);
HRESULT hr = m_IBandSite->GetBandObject(dwBandID, riid, ppv);
if (ppv) WrapLogPost("*ppv=%p\n", *ppv);
WrapLogExit("CMenuSiteWrap::GetBandObject()", hr);
@ -201,7 +201,7 @@ HRESULT STDMETHODCALLTYPE CMenuSiteWrap::GetBandSiteInfo(BANDSITEINFO *pbsinfo)
{
WrapLogEnter("CMenuSiteWrap<%p>::GetBandSiteInfo(BANDSITEINFO *pbsinfo=%p)\n", this, pbsinfo);
HRESULT hr = m_IBandSite->GetBandSiteInfo(pbsinfo);
if (pbsinfo) WrapLogPost("*pbsinfo=%p\n", *pbsinfo);
if (pbsinfo) WrapLogPost("*pbsinfo=%s\n", Wrap(*pbsinfo));
WrapLogExit("CMenuSiteWrap::GetBandSiteInfo()", hr);
return hr;
}
@ -236,7 +236,6 @@ HRESULT STDMETHODCALLTYPE CMenuSiteWrap::SetDeskBarSite(IUnknown *punkSite)
{
WrapLogEnter("CMenuSiteWrap<%p>::SetDeskBarSite(IUnknown *punkSite=%p)\n", this, punkSite);
HRESULT hr = m_IDeskBarClient->SetDeskBarSite(punkSite);
if (punkSite) WrapLogPost("*punkSite=%p\n", *punkSite);
WrapLogExit("CMenuSiteWrap::SetDeskBarSite()", hr);
return hr;
}