From ad29cd12e88745a43dd1ac970c5d5f1f10f0399a Mon Sep 17 00:00:00 2001 From: David Quintana Date: Mon, 12 Jan 2015 18:03:39 +0000 Subject: [PATCH] [RSHELL] * Overhaul the Popup method's position calculation. It now takes into account proper alignment preferences and exclusion rectangles in order to position menu popups better. * Fix a small compatibility issue with windows shell objects. * Use the item rectangle for the exclusion, so that the popup code can properly calculate how to flip the menu position if it doesn't fit downwards. [EXPLORER] * Fix the flags sent to Popup when showing the start menu. We have flags that say exactly what we want, and MPPF_ALIGN_LEFT/RIGHT were introduced with NT6 anyhow. CORE-9004 #resolve #comment Should be fixed with trunk r66030. svn path=/trunk/; revision=66030 --- reactos/base/shell/explorer/traywnd.cpp | 16 +- reactos/base/shell/rshell/CMenuDeskBar.cpp | 169 +++++++++++++++--- .../base/shell/rshell/CMenuFocusManager.cpp | 2 +- reactos/base/shell/rshell/CMenuToolbars.cpp | 26 +-- 4 files changed, 166 insertions(+), 47 deletions(-) diff --git a/reactos/base/shell/explorer/traywnd.cpp b/reactos/base/shell/explorer/traywnd.cpp index 2bd46aefbb9..1c9e4db4d34 100644 --- a/reactos/base/shell/explorer/traywnd.cpp +++ b/reactos/base/shell/explorer/traywnd.cpp @@ -1972,18 +1972,22 @@ ChangePos: case ABE_BOTTOM: pt.x = rcExclude.left; pt.y = rcExclude.top; - dwFlags |= MPPF_BOTTOM; + dwFlags |= MPPF_TOP; break; case ABE_TOP: - case ABE_LEFT: pt.x = rcExclude.left; pt.y = rcExclude.bottom; - dwFlags |= MPPF_TOP | MPPF_ALIGN_RIGHT; + dwFlags |= MPPF_BOTTOM; + break; + case ABE_LEFT: + pt.x = rcExclude.right; + pt.y = rcExclude.top; + dwFlags |= MPPF_RIGHT; break; case ABE_RIGHT: - pt.x = rcExclude.right; - pt.y = rcExclude.bottom; - dwFlags |= MPPF_TOP | MPPF_ALIGN_LEFT; + pt.x = rcExclude.left; + pt.y = rcExclude.top; + dwFlags |= MPPF_LEFT; break; } diff --git a/reactos/base/shell/rshell/CMenuDeskBar.cpp b/reactos/base/shell/rshell/CMenuDeskBar.cpp index d068283d54c..ecacf4b9721 100644 --- a/reactos/base/shell/rshell/CMenuDeskBar.cpp +++ b/reactos/base/shell/rshell/CMenuDeskBar.cpp @@ -268,6 +268,37 @@ HRESULT STDMETHODCALLTYPE CMenuDeskBar::GetSite(REFIID riid, void **ppvSite) return m_Site->QueryInterface(riid, ppvSite); } +static void AdjustForExcludeArea(BOOL alignLeft, BOOL alignTop, BOOL preferVertical, PINT px, PINT py, INT cx, INT cy, RECTL rcExclude) { + RECT rcWindow = { *px, *py, *px + cx, *py + cy }; + + if (rcWindow.right > rcExclude.left && rcWindow.left < rcExclude.right && + rcWindow.bottom > rcExclude.top && rcWindow.top < rcExclude.bottom) + { + if (preferVertical) + { + if (alignTop && rcWindow.bottom > rcExclude.top) + *py = rcExclude.top - cy; + else if (!alignTop && rcWindow.top < rcExclude.bottom) + *py = rcExclude.bottom; + else if (alignLeft && rcWindow.right > rcExclude.left) + *px = rcExclude.left - cx; + else if (!alignLeft && rcWindow.left < rcExclude.right) + *px = rcExclude.right; + } + else + { + if (alignLeft && rcWindow.right > rcExclude.left) + *px = rcExclude.left - cx; + else if (!alignLeft && rcWindow.left < rcExclude.right) + *px = rcExclude.right; + else if (alignTop && rcWindow.bottom > rcExclude.top) + *py = rcExclude.top - cy; + else if (!alignTop && rcWindow.top < rcExclude.bottom) + *py = rcExclude.bottom; + } + } +} + HRESULT STDMETHODCALLTYPE CMenuDeskBar::Popup(POINTL *ppt, RECTL *prcExclude, MP_POPUPFLAGS dwFlags) { HRESULT hr; @@ -321,59 +352,143 @@ HRESULT STDMETHODCALLTYPE CMenuDeskBar::Popup(POINTL *ppt, RECTL *prcExclude, MP RECT rcWorkArea; GetWindowRect(GetDesktopWindow(), &rcWorkArea); - int waHeight = rcWorkArea.bottom - rcWorkArea.top; + int cxWorkArea = rcWorkArea.right - rcWorkArea.left; + int cyWorkArea = rcWorkArea.bottom - rcWorkArea.top; int x = ppt->x; int y = ppt->y; int cx = rc.right - rc.left; int cy = rc.bottom - rc.top; - switch (dwFlags & 0xFF000000) + // TODO: Make alignLeft default to TRUE in LTR systems or whenever necessary. + BOOL alignLeft = FALSE; + BOOL alignTop = FALSE; + BOOL preferVertical = FALSE; + switch (dwFlags & MPPF_POS_MASK) { + case MPPF_TOP: + alignTop = TRUE; + preferVertical = TRUE; + break; + case MPPF_LEFT: + alignLeft = TRUE; + break; case MPPF_BOTTOM: - x = ppt->x; - y = ppt->y - rc.bottom; + alignTop = FALSE; + preferVertical = TRUE; break; case MPPF_RIGHT: - x = ppt->x + rc.left; - y = ppt->y + rc.top; - break; - case MPPF_TOP | MPPF_ALIGN_LEFT: - x = ppt->x - rc.right; - y = ppt->y + rc.top; - break; - case MPPF_TOP | MPPF_ALIGN_RIGHT: - x = ppt->x; - y = ppt->y + rc.top; + alignLeft = FALSE; break; } + // Try the selected alignment and verify that it doesn't escape the work area. + if (alignLeft) + { + x = ppt->x - cx; + } + else + { + x = ppt->x; + } + + if (alignTop) + { + y = ppt->y - cy; + } + else + { + y = ppt->y; + } + + if (prcExclude) + AdjustForExcludeArea(alignLeft, alignTop, preferVertical, &x, &y, cx, cy, *prcExclude); + + // Verify that it doesn't escape the work area, and flip. + if (alignLeft) + { + if (x < rcWorkArea.left && (ppt->x+cx) <= rcWorkArea.right) + { + alignLeft = FALSE; + if (prcExclude) + x = prcExclude->right - ((x + cx) - prcExclude->left); + else + x = ppt->x; + } + } + else + { + if ((ppt->x + cx) > rcWorkArea.right && x >= rcWorkArea.left) + { + alignLeft = TRUE; + if (prcExclude) + x = prcExclude->left - cx + (prcExclude->right - x); + else + x = ppt->x - cx; + } + } + + BOOL flipV = FALSE; + if (alignTop) + { + if (y < rcWorkArea.top && (ppt->y + cy) <= rcWorkArea.bottom) + { + alignTop = FALSE; + if (prcExclude) + y = prcExclude->bottom - ((y + cy) - prcExclude->top); + else + y = ppt->y; + + flipV = true; + } + } + else + { + if ((ppt->y + cy) > rcWorkArea.bottom && y >= rcWorkArea.top) + { + alignTop = TRUE; + if (prcExclude) + y = prcExclude->top - cy + (prcExclude->bottom - y); + else + y = ppt->y - cy; + + flipV = true; + } + } + + if (prcExclude) + AdjustForExcludeArea(alignLeft, alignTop, preferVertical, &x, &y, cx, cy, *prcExclude); + + if (x < rcWorkArea.left) + x = rcWorkArea.left; + + if (cx > cxWorkArea) + cx = cxWorkArea; + if (x + cx > rcWorkArea.right) - { - // FIXME: Works, but it's oversimplified. - x = prcExclude->left - cx; - dwFlags = (dwFlags & (~MPPF_TOP)) | MPPF_LEFT; - } + x = rcWorkArea.right - cx; if (y < rcWorkArea.top) - { y = rcWorkArea.top; - } - if (cy > waHeight) - { - cy = waHeight; - } + if (cy > cyWorkArea) + cy = cyWorkArea; if (y + cy > rcWorkArea.bottom) - { y = rcWorkArea.bottom - cy; - } int flags = SWP_SHOWWINDOW | SWP_NOACTIVATE; this->SetWindowPos(HWND_TOPMOST, x, y, cx, cy, flags); + if (flipV) + { + if (dwFlags & MPPF_INITIALSELECT) + dwFlags = (dwFlags ^ MPPF_INITIALSELECT) | MPPF_FINALSELECT; + else if (dwFlags & MPPF_FINALSELECT) + dwFlags = (dwFlags ^ MPPF_FINALSELECT) | MPPF_INITIALSELECT; + } + m_ShowFlags = dwFlags; m_Shown = true; diff --git a/reactos/base/shell/rshell/CMenuFocusManager.cpp b/reactos/base/shell/rshell/CMenuFocusManager.cpp index 4cf9b6b5260..2a964a46c1e 100644 --- a/reactos/base/shell/rshell/CMenuFocusManager.cpp +++ b/reactos/base/shell/rshell/CMenuFocusManager.cpp @@ -777,7 +777,7 @@ HRESULT CMenuFocusManager::UpdateFocus() CComPtr bandSite; CComPtr deskBar; hr = topMenu->mb->GetSite(IID_PPV_ARG(IServiceProvider, &bandSite)); - hr = bandSite->QueryService(SID_SMenuBandParent, IID_PPV_ARG(IOleWindow, &deskBar)); + hr = bandSite->QueryService(SID_SMenuPopup, IID_PPV_ARG(IOleWindow, &deskBar)); CComPtr deskBarSite; hr = IUnknown_GetSite(deskBar, IID_PPV_ARG(IOleWindow, &deskBarSite)); diff --git a/reactos/base/shell/rshell/CMenuToolbars.cpp b/reactos/base/shell/rshell/CMenuToolbars.cpp index 466f0f7cb80..40427c2881f 100644 --- a/reactos/base/shell/rshell/CMenuToolbars.cpp +++ b/reactos/base/shell/rshell/CMenuToolbars.cpp @@ -684,32 +684,32 @@ HRESULT CMenuToolbarBase::PopupSubMenu(UINT iItem, UINT index, IShellMenu* child { // Calculate the submenu position and exclude area RECT rc = { 0 }; - RECT rcx = { 0 }; if (!GetItemRect(index, &rc)) return E_FAIL; - - HWND topWnd; - GetWindow(&topWnd); - GetWindowRect(topWnd, &rcx); - + POINT a = { rc.left, rc.top }; POINT b = { rc.right, rc.bottom }; - POINT c = { rcx.left, rcx.top }; - POINT d = { rcx.right, rcx.bottom }; ClientToScreen(m_hWnd, &a); ClientToScreen(m_hWnd, &b); - ClientToScreen(topWnd, &c); - ClientToScreen(topWnd, &d); POINTL pt = { a.x, b.y }; - RECTL rcl = { c.x, c.y, d.x, d.y }; + RECTL rcl = { a.x, a.y, b.x, b.y }; if (m_initFlags & SMINIT_VERTICAL) { - pt.x = b.x - 3; - pt.y = a.y - 3; + // FIXME: Hardcoding this here feels hacky. + if (IsAppThemed()) + { + pt.x = b.x - 1; + pt.y = a.y - 1; + } + else + { + pt.x = b.x - 3; + pt.y = a.y - 3; + } } // Display the submenu