https://git.reactos.org/?p=reactos.git;a=commitdiff;h=a59df3858cb3718be710a8...
commit a59df3858cb3718be710a8782209c6d3c8462913 Author: Mark Jansen mark.jansen@reactos.org AuthorDate: Sat Jun 15 21:14:41 2019 +0200 Commit: Mark Jansen mark.jansen@reactos.org CommitDate: Tue Aug 6 21:17:35 2019 +0200
[WIN32SS] Rewrite MENU_ShowPopup to take the exclude rectangle into account CORE-15863 --- win32ss/user/ntuser/menu.c | 195 +++++++++++++++++++++++++++++++++------------ 1 file changed, 146 insertions(+), 49 deletions(-)
diff --git a/win32ss/user/ntuser/menu.c b/win32ss/user/ntuser/menu.c index ccd4d8cc058..c73151427c6 100644 --- a/win32ss/user/ntuser/menu.c +++ b/win32ss/user/ntuser/menu.c @@ -2860,16 +2860,57 @@ static BOOL MENU_InitPopup( PWND pWndOwner, PMENU menu, UINT flags ) return TRUE; }
+ +#define SHOW_DEBUGRECT 0 + +#if SHOW_DEBUGRECT +static void DebugRect(const RECT* rectl, COLORREF color) +{ + HBRUSH brush; + RECT rr; + HDC hdc; + + if (!rectl) + return; + + hdc = UserGetDCEx(NULL, 0, DCX_USESTYLE); + + brush = IntGdiCreateSolidBrush(color); + + rr = *rectl; + RECTL_vInflateRect(&rr, 1, 1); + FrameRect(hdc, rectl, brush); + FrameRect(hdc, &rr, brush); + + NtGdiDeleteObjectApp(brush); + UserReleaseDC(NULL, hdc, TRUE); +} + +static void DebugPoint(INT x, INT y, COLORREF color) +{ + RECT rr = {x, y, x, y}; + DebugRect(&rr, color); +} +#endif + +static BOOL RECTL_Intersect(const RECT* pRect, INT x, INT y, UINT width, UINT height) +{ + RECT other = {x, y, x + width, y + height}; + RECT dum; + + return RECTL_bIntersectRect(&dum, pRect, &other); +} + /*********************************************************************** * MenuShowPopup * * Display a popup menu. */ static BOOL FASTCALL MENU_ShowPopup(PWND pwndOwner, PMENU menu, UINT id, UINT flags, - INT x, INT y) + INT x, INT y, const RECT* pExclude) { UINT width, height; - POINT pt; + POINT ptx; PMONITOR monitor; PWND pWnd; USER_REFERENCE_ENTRY Ref; @@ -2884,6 +2925,11 @@ static BOOL FASTCALL MENU_ShowPopup(PWND pwndOwner, PMENU menu, UINT id, UINT fl menu->iItem = NO_SELECTED_ITEM; }
+#if SHOW_DEBUGRECT + if (pExclude) + DebugRect(pExclude, RGB(255, 0, 0)); +#endif + menu->dwArrowsOn = 0; MENU_PopupMenuCalcSize(menu, pwndOwner);
@@ -2892,61 +2938,106 @@ static BOOL FASTCALL MENU_ShowPopup(PWND pwndOwner, PMENU menu, UINT id, UINT fl width = menu->cxMenu + UserGetSystemMetrics(SM_CXBORDER); height = menu->cyMenu + UserGetSystemMetrics(SM_CYBORDER);
- /* FIXME: should use item rect */ - pt.x = x; - pt.y = y; - monitor = UserMonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST ); - if (flags & TPM_LAYOUTRTL) flags ^= TPM_RIGHTALIGN;
- if( flags & TPM_RIGHTALIGN ) x -= width; - if( flags & TPM_CENTERALIGN ) x -= width / 2; + if (flags & TPM_RIGHTALIGN) + x -= width; + if (flags & TPM_CENTERALIGN) + x -= width / 2;
- if( flags & TPM_BOTTOMALIGN ) y -= height; - if( flags & TPM_VCENTERALIGN ) y -= height / 2; + if (flags & TPM_BOTTOMALIGN) + y -= height; + if (flags & TPM_VCENTERALIGN) + y -= height / 2;
- if( x + width > monitor->rcMonitor.right) + /* FIXME: should use item rect */ + ptx.x = x; + ptx.y = y; +#if SHOW_DEBUGRECT + DebugPoint(x, y, RGB(0, 0, 255)); +#endif + monitor = UserMonitorFromPoint( ptx, MONITOR_DEFAULTTONEAREST ); + + /* We are off the right side of the screen */ + if (x + width > monitor->rcMonitor.right) { - if( x + width > monitor->rcMonitor.right) - { - /* If we would flip around our origin, would we go off screen on the other side? - Or is our origin itself too far to the right already? */ - if (!bIsPopup || x - width < monitor->rcMonitor.left || x > monitor->rcMonitor.right) - x = monitor->rcMonitor.right - width; - else - x -= width; - } + if ((x - width) < monitor->rcMonitor.left || x >= monitor->rcMonitor.right || bIsPopup) + x = monitor->rcMonitor.right - width; + else + x -= width; } - if( x < monitor->rcMonitor.left ) + + /* We are off the left side of the screen */ + if (x < monitor->rcMonitor.left) { - /* If we would flip around our origin, would we go off screen on the other side? */ - if (!bIsPopup || x + width > monitor->rcMonitor.right) + /* Re-orient the menu around the x-axis */ + x += width; + + if (x < monitor->rcMonitor.left || x >= monitor->rcMonitor.right || bIsPopup) x = monitor->rcMonitor.left; + } + + /* Same here, but then the top */ + if (y < monitor->rcMonitor.top) + { + y += height; + + if (y < monitor->rcMonitor.top || y >= monitor->rcMonitor.bottom || bIsPopup) + y = monitor->rcMonitor.top; + } + + /* And the bottom */ + if (y + height > monitor->rcMonitor.bottom) + { + if ((y - height) < monitor->rcMonitor.top || y >= monitor->rcMonitor.bottom || bIsPopup) + y = monitor->rcMonitor.bottom - height; else - x += width; + y -= height; }
- if( y + height > monitor->rcMonitor.bottom) + if (pExclude || bIsPopup) { - if( y + height > monitor->rcMonitor.bottom) + RECT PopupOrigin = {x-1, y-1, x+1, y+1}; + RECT Cleaned; + + if (RECTL_bIntersectRect(&Cleaned, pExclude ? pExclude : &PopupOrigin, &monitor->rcMonitor) && + RECTL_Intersect(&Cleaned, x, y, width, height)) { - /* If we would flip around our origin, would we go off screen on the other side? - Or is our origin itself too far to the bottom already? */ - if (!bIsPopup || y - height < monitor->rcMonitor.top || y > monitor->rcMonitor.bottom) - y = monitor->rcMonitor.bottom - height; + /* Figure out if we should move vertical or horizontal */ + if (flags & TPM_VERTICAL) + { + /* Move in the vertical direction: TPM_BOTTOMALIGN means drop it above, otherways drop it below */ + if (flags & TPM_BOTTOMALIGN) + { + y = Cleaned.top - height; + } + else + { + y = Cleaned.bottom; + } + } else - y -= height; + { + /* Move in the horizontal direction: TPM_RIGHTALIGN means drop it to the left, otherways go right */ + if (flags & TPM_RIGHTALIGN) + { + x = Cleaned.left - width; + } + else + { + x = Cleaned.right; + } + } } } - if( y < monitor->rcMonitor.top ) + +#if SHOW_DEBUGRECT { - /* If we would flip around our origin, would we go off screen on the other side? */ - if (!bIsPopup || y + height > monitor->rcMonitor.bottom) - y = monitor->rcMonitor.top; - else - y += height; + RECT rr = {x, y, x + width, y + height}; + DebugRect(&rr, RGB(0, 255, 0)); } +#endif
pWnd = ValidateHwndNoErr( menu->hWnd );
@@ -3194,7 +3285,7 @@ static void FASTCALL MENU_HideSubPopups(PWND pWndOwner, PMENU Menu, */ static PMENU FASTCALL MENU_ShowSubPopup(PWND WndOwner, PMENU Menu, BOOL SelectFirst, UINT Flags) { - RECT Rect; + RECT Rect, ParentRect; ITEM *Item; HDC Dc; PWND pWnd; @@ -3229,6 +3320,14 @@ static PMENU FASTCALL MENU_ShowSubPopup(PWND WndOwner, PMENU Menu, BOOL SelectFi
pWnd = ValidateHwndNoErr(Menu->hWnd);
+ ParentRect = Rect; + if (pWnd) + { + POINT pt = {0, 0}; + IntClientToScreen(pWnd, &pt); + RECTL_vOffsetRect(&ParentRect, pt.x, pt.y); + } + /* correct item if modified as a reaction to WM_INITMENUPOPUP message */ if (!(Item->fState & MF_HILITE)) { @@ -3305,7 +3404,7 @@ static PMENU FASTCALL MENU_ShowSubPopup(PWND WndOwner, PMENU Menu, BOOL SelectFi MENU_InitPopup( WndOwner, Item->spSubMenu, Flags );
MENU_ShowPopup( WndOwner, Item->spSubMenu, Menu->iItem, Flags, - Rect.left, Rect.top); + Rect.left, Rect.top, &ParentRect); if (SelectFirst) { MENU_MoveSelection(WndOwner, Item->spSubMenu, ITEM_NEXT); @@ -3905,7 +4004,7 @@ static void FASTCALL MENU_KeyRight(MTRACKER *pmt, UINT Flags, UINT msg) * Menu tracking code. */ static INT FASTCALL MENU_TrackMenu(PMENU pmenu, UINT wFlags, INT x, INT y, - PWND pwnd, const RECT *lprect ) + PWND pwnd) { MSG msg; BOOL fRemove; @@ -3928,9 +4027,8 @@ static INT FASTCALL MENU_TrackMenu(PMENU pmenu, UINT wFlags, INT x, INT y, mt.Pt.x = x; mt.Pt.y = y;
- TRACE("MTM : hmenu=%p flags=0x%08x (%d,%d) hwnd=%x (%ld,%ld)-(%ld,%ld)\n", - UserHMGetHandle(pmenu), wFlags, x, y, UserHMGetHandle(pwnd), lprect ? lprect->left : 0, lprect ? lprect->top : 0, - lprect ? lprect->right : 0, lprect ? lprect->bottom : 0); + TRACE("MTM : hmenu=%p flags=0x%08x (%d,%d) hwnd=%x\n", + UserHMGetHandle(pmenu), wFlags, x, y, UserHMGetHandle(pwnd));
pti->MessageQueue->QF_flags &= ~QF_ACTIVATIONCHANGE;
@@ -4341,7 +4439,7 @@ VOID MENU_TrackMouseMenuBar( PWND pWnd, ULONG ht, POINT pt) MENU_InitTracking(pWnd, pMenu, FALSE, wFlags); /* fetch the window menu again, it may have changed */ pMenu = (ht == HTSYSMENU) ? get_win_sys_menu( UserHMGetHandle(pWnd) ) : IntGetMenu( UserHMGetHandle(pWnd) ); - MENU_TrackMenu(pMenu, wFlags, pt.x, pt.y, pWnd, NULL); + MENU_TrackMenu(pMenu, wFlags, pt.x, pt.y, pWnd); MENU_ExitTracking(pWnd, FALSE, wFlags); } } @@ -4405,7 +4503,7 @@ VOID MENU_TrackKbdMenuBar(PWND pwnd, UINT wParam, WCHAR wChar) }
track_menu: - MENU_TrackMenu( TrackMenu, wFlags, 0, 0, pwnd, NULL ); + MENU_TrackMenu( TrackMenu, wFlags, 0, 0, pwnd ); MENU_ExitTracking( pwnd, FALSE, wFlags); }
@@ -4447,9 +4545,8 @@ BOOL WINAPI IntTrackPopupMenuEx( PMENU menu, UINT wFlags, int x, int y, if (menu->fFlags & MNF_SYSMENU) MENU_InitSysMenuPopup( menu, pWnd->style, pWnd->pcls->style, HTSYSMENU);
- if (MENU_ShowPopup(pWnd, menu, 0, wFlags | TPM_POPUPMENU, x, y)) - ret = MENU_TrackMenu( menu, wFlags | TPM_POPUPMENU, 0, 0, pWnd, - lpTpm ? &lpTpm->rcExclude : NULL); + if (MENU_ShowPopup(pWnd, menu, 0, wFlags | TPM_POPUPMENU, x, y, lpTpm ? &lpTpm->rcExclude : NULL)) + ret = MENU_TrackMenu( menu, wFlags | TPM_POPUPMENU, 0, 0, pWnd); else { MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL);