Skip to content

Commit 3113d13

Browse files
committed
[0.4.12][WIN32SS] Improve MENU_ShowPopup()
to fix regression CORE-15863 This is the work that Mark jansen committed into master 0.4.13-dev-791-g a59df38 and 0.4.13-dev-792-g 7c45a64 and 0.4.13-dev-793-g b5c6af4 squashed into one single commit for 0.4.12RC. It's not perfect yet anymore for positioning the popup for systray icons if they contain many entries. And also not perfect when pressing the context menu key on desktop after a .lnk has been started, but overall gives a better user-experience for nested popups on the desktop, which is much more important. We can build up on top of it later in master.
1 parent 625a5a0 commit 3113d13

File tree

1 file changed

+188
-50
lines changed

1 file changed

+188
-50
lines changed

win32ss/user/ntuser/menu.c

Lines changed: 188 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2860,16 +2860,103 @@ static BOOL MENU_InitPopup( PWND pWndOwner, PMENU menu, UINT flags )
28602860
return TRUE;
28612861
}
28622862

2863+
2864+
#define SHOW_DEBUGRECT 0
2865+
2866+
#if SHOW_DEBUGRECT
2867+
static void DebugRect(const RECT* rectl, COLORREF color)
2868+
{
2869+
HBRUSH brush;
2870+
RECT rr;
2871+
HDC hdc;
2872+
2873+
if (!rectl)
2874+
return;
2875+
2876+
hdc = UserGetDCEx(NULL, 0, DCX_USESTYLE);
2877+
2878+
brush = IntGdiCreateSolidBrush(color);
2879+
2880+
rr = *rectl;
2881+
RECTL_vInflateRect(&rr, 1, 1);
2882+
FrameRect(hdc, rectl, brush);
2883+
FrameRect(hdc, &rr, brush);
2884+
2885+
NtGdiDeleteObjectApp(brush);
2886+
UserReleaseDC(NULL, hdc, TRUE);
2887+
}
2888+
2889+
static void DebugPoint(INT x, INT y, COLORREF color)
2890+
{
2891+
RECT rr = {x, y, x, y};
2892+
DebugRect(&rr, color);
2893+
}
2894+
#endif
2895+
2896+
static BOOL RECTL_Intersect(const RECT* pRect, INT x, INT y, UINT width, UINT height)
2897+
{
2898+
RECT other = {x, y, x + width, y + height};
2899+
RECT dum;
2900+
2901+
return RECTL_bIntersectRect(&dum, pRect, &other);
2902+
}
2903+
2904+
static BOOL MENU_MoveRect(UINT flags, INT* x, INT* y, INT width, INT height, const RECT* pExclude, PMONITOR monitor)
2905+
{
2906+
/* Figure out if we should move vertical or horizontal */
2907+
if (flags & TPM_VERTICAL)
2908+
{
2909+
/* Move in the vertical direction: TPM_BOTTOMALIGN means drop it above, otherways drop it below */
2910+
if (flags & TPM_BOTTOMALIGN)
2911+
{
2912+
if (pExclude->top - height >= monitor->rcMonitor.top)
2913+
{
2914+
*y = pExclude->top - height;
2915+
return TRUE;
2916+
}
2917+
}
2918+
else
2919+
{
2920+
if (pExclude->bottom + height < monitor->rcMonitor.bottom)
2921+
{
2922+
*y = pExclude->bottom;
2923+
return TRUE;
2924+
}
2925+
}
2926+
}
2927+
else
2928+
{
2929+
/* Move in the horizontal direction: TPM_RIGHTALIGN means drop it to the left, otherways go right */
2930+
if (flags & TPM_RIGHTALIGN)
2931+
{
2932+
if (pExclude->left - width >= monitor->rcMonitor.left)
2933+
{
2934+
*x = pExclude->left - width;
2935+
return TRUE;
2936+
}
2937+
}
2938+
else
2939+
{
2940+
if (pExclude->right + width < monitor->rcMonitor.right)
2941+
{
2942+
*x = pExclude->right;
2943+
return TRUE;
2944+
}
2945+
}
2946+
}
2947+
return FALSE;
2948+
}
2949+
28632950
/***********************************************************************
28642951
* MenuShowPopup
28652952
*
28662953
* Display a popup menu.
28672954
*/
28682955
static BOOL FASTCALL MENU_ShowPopup(PWND pwndOwner, PMENU menu, UINT id, UINT flags,
2869-
INT x, INT y)
2956+
INT x, INT y, const RECT* pExclude)
28702957
{
28712958
UINT width, height;
2872-
POINT pt;
2959+
POINT ptx;
28732960
PMONITOR monitor;
28742961
PWND pWnd;
28752962
USER_REFERENCE_ENTRY Ref;
@@ -2884,6 +2971,11 @@ static BOOL FASTCALL MENU_ShowPopup(PWND pwndOwner, PMENU menu, UINT id, UINT fl
28842971
menu->iItem = NO_SELECTED_ITEM;
28852972
}
28862973

2974+
#if SHOW_DEBUGRECT
2975+
if (pExclude)
2976+
DebugRect(pExclude, RGB(255, 0, 0));
2977+
#endif
2978+
28872979
menu->dwArrowsOn = 0;
28882980
MENU_PopupMenuCalcSize(menu, pwndOwner);
28892981

@@ -2892,61 +2984,102 @@ static BOOL FASTCALL MENU_ShowPopup(PWND pwndOwner, PMENU menu, UINT id, UINT fl
28922984
width = menu->cxMenu + UserGetSystemMetrics(SM_CXBORDER);
28932985
height = menu->cyMenu + UserGetSystemMetrics(SM_CYBORDER);
28942986

2895-
/* FIXME: should use item rect */
2896-
pt.x = x;
2897-
pt.y = y;
2898-
monitor = UserMonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
2899-
29002987
if (flags & TPM_LAYOUTRTL)
29012988
flags ^= TPM_RIGHTALIGN;
29022989

2903-
if( flags & TPM_RIGHTALIGN ) x -= width;
2904-
if( flags & TPM_CENTERALIGN ) x -= width / 2;
2990+
if (flags & TPM_RIGHTALIGN)
2991+
x -= width;
2992+
if (flags & TPM_CENTERALIGN)
2993+
x -= width / 2;
29052994

2906-
if( flags & TPM_BOTTOMALIGN ) y -= height;
2907-
if( flags & TPM_VCENTERALIGN ) y -= height / 2;
2995+
if (flags & TPM_BOTTOMALIGN)
2996+
y -= height;
2997+
if (flags & TPM_VCENTERALIGN)
2998+
y -= height / 2;
29082999

2909-
if( x + width > monitor->rcMonitor.right)
3000+
/* FIXME: should use item rect */
3001+
ptx.x = x;
3002+
ptx.y = y;
3003+
#if SHOW_DEBUGRECT
3004+
DebugPoint(x, y, RGB(0, 0, 255));
3005+
#endif
3006+
monitor = UserMonitorFromPoint( ptx, MONITOR_DEFAULTTONEAREST );
3007+
3008+
/* We are off the right side of the screen */
3009+
if (x + width > monitor->rcMonitor.right)
29103010
{
2911-
if( x + width > monitor->rcMonitor.right)
2912-
{
2913-
/* If we would flip around our origin, would we go off screen on the other side?
2914-
Or is our origin itself too far to the right already? */
2915-
if (!bIsPopup || x - width < monitor->rcMonitor.left || x > monitor->rcMonitor.right)
2916-
x = monitor->rcMonitor.right - width;
2917-
else
2918-
x -= width;
2919-
}
3011+
if ((x - width) < monitor->rcMonitor.left || x >= monitor->rcMonitor.right)
3012+
x = monitor->rcMonitor.right - width;
3013+
else
3014+
x -= width;
29203015
}
2921-
if( x < monitor->rcMonitor.left )
3016+
3017+
/* We are off the left side of the screen */
3018+
if (x < monitor->rcMonitor.left)
29223019
{
2923-
/* If we would flip around our origin, would we go off screen on the other side? */
2924-
if (!bIsPopup || x + width > monitor->rcMonitor.right)
3020+
/* Re-orient the menu around the x-axis */
3021+
x += width;
3022+
3023+
if (x < monitor->rcMonitor.left || x >= monitor->rcMonitor.right || bIsPopup)
29253024
x = monitor->rcMonitor.left;
3025+
}
3026+
3027+
/* Same here, but then the top */
3028+
if (y < monitor->rcMonitor.top)
3029+
{
3030+
y += height;
3031+
3032+
if (y < monitor->rcMonitor.top || y >= monitor->rcMonitor.bottom || bIsPopup)
3033+
y = monitor->rcMonitor.top;
3034+
}
3035+
3036+
/* And the bottom */
3037+
if (y + height > monitor->rcMonitor.bottom)
3038+
{
3039+
if ((y - height) < monitor->rcMonitor.top || y >= monitor->rcMonitor.bottom || bIsPopup)
3040+
y = monitor->rcMonitor.bottom - height;
29263041
else
2927-
x += width;
3042+
y -= height;
29283043
}
29293044

2930-
if( y + height > monitor->rcMonitor.bottom)
3045+
if (pExclude)
29313046
{
2932-
if( y + height > monitor->rcMonitor.bottom)
3047+
RECT Cleaned;
3048+
3049+
if (RECTL_bIntersectRect(&Cleaned, pExclude, &monitor->rcMonitor) &&
3050+
RECTL_Intersect(&Cleaned, x, y, width, height))
29333051
{
2934-
/* If we would flip around our origin, would we go off screen on the other side?
2935-
Or is our origin itself too far to the bottom already? */
2936-
if (!bIsPopup || y - height < monitor->rcMonitor.top || y > monitor->rcMonitor.bottom)
2937-
y = monitor->rcMonitor.bottom - height;
2938-
else
2939-
y -= height;
3052+
UINT flag_mods[] = {
3053+
0, /* First try the 'normal' way */
3054+
TPM_BOTTOMALIGN | TPM_RIGHTALIGN, /* Then try the opposite side */
3055+
TPM_VERTICAL, /* Then swap horizontal / vertical */
3056+
TPM_BOTTOMALIGN | TPM_RIGHTALIGN | TPM_VERTICAL, /* Then the other side again (still swapped hor/ver) */
3057+
};
3058+
UINT n;
3059+
for (n = 0; n < RTL_NUMBER_OF(flag_mods); ++n)
3060+
{
3061+
INT tx = x;
3062+
INT ty = y;
3063+
3064+
/* Try to move a bit around */
3065+
if (MENU_MoveRect(flags ^ flag_mods[n], &tx, &ty, width, height, &Cleaned, monitor) &&
3066+
!RECTL_Intersect(&Cleaned, tx, ty, width, height))
3067+
{
3068+
x = tx;
3069+
y = ty;
3070+
break;
3071+
}
3072+
}
3073+
/* If none worked, we go with the original x/y */
29403074
}
29413075
}
2942-
if( y < monitor->rcMonitor.top )
3076+
3077+
#if SHOW_DEBUGRECT
29433078
{
2944-
/* If we would flip around our origin, would we go off screen on the other side? */
2945-
if (!bIsPopup || y + height > monitor->rcMonitor.bottom)
2946-
y = monitor->rcMonitor.top;
2947-
else
2948-
y += height;
3079+
RECT rr = {x, y, x + width, y + height};
3080+
DebugRect(&rr, RGB(0, 255, 0));
29493081
}
3082+
#endif
29503083

29513084
pWnd = ValidateHwndNoErr( menu->hWnd );
29523085

@@ -3194,7 +3327,7 @@ static void FASTCALL MENU_HideSubPopups(PWND pWndOwner, PMENU Menu,
31943327
*/
31953328
static PMENU FASTCALL MENU_ShowSubPopup(PWND WndOwner, PMENU Menu, BOOL SelectFirst, UINT Flags)
31963329
{
3197-
RECT Rect;
3330+
RECT Rect, ParentRect;
31983331
ITEM *Item;
31993332
HDC Dc;
32003333
PWND pWnd;
@@ -3229,6 +3362,13 @@ static PMENU FASTCALL MENU_ShowSubPopup(PWND WndOwner, PMENU Menu, BOOL SelectFi
32293362

32303363
pWnd = ValidateHwndNoErr(Menu->hWnd);
32313364

3365+
/* Grab the rect of our (entire) parent menu, so we can try to not overlap it */
3366+
if (!IntGetWindowRect(pWnd, &ParentRect))
3367+
{
3368+
ERR("No pWnd\n");
3369+
ParentRect = Rect;
3370+
}
3371+
32323372
/* correct item if modified as a reaction to WM_INITMENUPOPUP message */
32333373
if (!(Item->fState & MF_HILITE))
32343374
{
@@ -3305,7 +3445,7 @@ static PMENU FASTCALL MENU_ShowSubPopup(PWND WndOwner, PMENU Menu, BOOL SelectFi
33053445
MENU_InitPopup( WndOwner, Item->spSubMenu, Flags );
33063446

33073447
MENU_ShowPopup( WndOwner, Item->spSubMenu, Menu->iItem, Flags,
3308-
Rect.left, Rect.top);
3448+
Rect.left, Rect.top, &ParentRect);
33093449
if (SelectFirst)
33103450
{
33113451
MENU_MoveSelection(WndOwner, Item->spSubMenu, ITEM_NEXT);
@@ -3905,7 +4045,7 @@ static void FASTCALL MENU_KeyRight(MTRACKER *pmt, UINT Flags, UINT msg)
39054045
* Menu tracking code.
39064046
*/
39074047
static INT FASTCALL MENU_TrackMenu(PMENU pmenu, UINT wFlags, INT x, INT y,
3908-
PWND pwnd, const RECT *lprect )
4048+
PWND pwnd)
39094049
{
39104050
MSG msg;
39114051
BOOL fRemove;
@@ -3928,9 +4068,8 @@ static INT FASTCALL MENU_TrackMenu(PMENU pmenu, UINT wFlags, INT x, INT y,
39284068
mt.Pt.x = x;
39294069
mt.Pt.y = y;
39304070

3931-
TRACE("MTM : hmenu=%p flags=0x%08x (%d,%d) hwnd=%x (%ld,%ld)-(%ld,%ld)\n",
3932-
UserHMGetHandle(pmenu), wFlags, x, y, UserHMGetHandle(pwnd), lprect ? lprect->left : 0, lprect ? lprect->top : 0,
3933-
lprect ? lprect->right : 0, lprect ? lprect->bottom : 0);
4071+
TRACE("MTM : hmenu=%p flags=0x%08x (%d,%d) hwnd=%x\n",
4072+
UserHMGetHandle(pmenu), wFlags, x, y, UserHMGetHandle(pwnd));
39344073

39354074
pti->MessageQueue->QF_flags &= ~QF_ACTIVATIONCHANGE;
39364075

@@ -4341,7 +4480,7 @@ VOID MENU_TrackMouseMenuBar( PWND pWnd, ULONG ht, POINT pt)
43414480
MENU_InitTracking(pWnd, pMenu, FALSE, wFlags);
43424481
/* fetch the window menu again, it may have changed */
43434482
pMenu = (ht == HTSYSMENU) ? get_win_sys_menu( UserHMGetHandle(pWnd) ) : IntGetMenu( UserHMGetHandle(pWnd) );
4344-
MENU_TrackMenu(pMenu, wFlags, pt.x, pt.y, pWnd, NULL);
4483+
MENU_TrackMenu(pMenu, wFlags, pt.x, pt.y, pWnd);
43454484
MENU_ExitTracking(pWnd, FALSE, wFlags);
43464485
}
43474486
}
@@ -4405,7 +4544,7 @@ VOID MENU_TrackKbdMenuBar(PWND pwnd, UINT wParam, WCHAR wChar)
44054544
}
44064545

44074546
track_menu:
4408-
MENU_TrackMenu( TrackMenu, wFlags, 0, 0, pwnd, NULL );
4547+
MENU_TrackMenu( TrackMenu, wFlags, 0, 0, pwnd );
44094548
MENU_ExitTracking( pwnd, FALSE, wFlags);
44104549
}
44114550

@@ -4447,9 +4586,8 @@ BOOL WINAPI IntTrackPopupMenuEx( PMENU menu, UINT wFlags, int x, int y,
44474586
if (menu->fFlags & MNF_SYSMENU)
44484587
MENU_InitSysMenuPopup( menu, pWnd->style, pWnd->pcls->style, HTSYSMENU);
44494588

4450-
if (MENU_ShowPopup(pWnd, menu, 0, wFlags | TPM_POPUPMENU, x, y))
4451-
ret = MENU_TrackMenu( menu, wFlags | TPM_POPUPMENU, 0, 0, pWnd,
4452-
lpTpm ? &lpTpm->rcExclude : NULL);
4589+
if (MENU_ShowPopup(pWnd, menu, 0, wFlags | TPM_POPUPMENU, x, y, lpTpm ? &lpTpm->rcExclude : NULL))
4590+
ret = MENU_TrackMenu( menu, wFlags | TPM_POPUPMENU, 0, 0, pWnd);
44534591
else
44544592
{
44554593
MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL);

0 commit comments

Comments
 (0)