user: Implement scrolling in popup menus.

This commit is contained in:
Phil Krylov 2006-02-17 18:34:31 +01:00 committed by Alexandre Julliard
parent 1269f43c5a
commit dafa5bf464
1 changed files with 214 additions and 14 deletions

View File

@ -5,6 +5,7 @@
* Copyright 1994 Alexandre Julliard * Copyright 1994 Alexandre Julliard
* Copyright 1997 Morten Welinder * Copyright 1997 Morten Welinder
* Copyright 2005 Maxime Bellengé * Copyright 2005 Maxime Bellengé
* Copyright 2006 Phil Krylov
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -93,9 +94,12 @@ typedef struct {
UINT FocusedItem; /* Currently focused item */ UINT FocusedItem; /* Currently focused item */
HWND hwndOwner; /* window receiving the messages for ownerdraw */ HWND hwndOwner; /* window receiving the messages for ownerdraw */
BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */ BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
BOOL bScrolling; /* Scroll arrows are active */
UINT nScrollPos; /* Current scroll position */
UINT nTotalHeight; /* Total height of menu items inside menu */
/* ------------ MENUINFO members ------ */ /* ------------ MENUINFO members ------ */
DWORD dwStyle; /* Extended mennu style */ DWORD dwStyle; /* Extended menu style */
UINT cyMax; /* max hight of the whole menu, 0 is screen hight */ UINT cyMax; /* max height of the whole menu, 0 is screen height */
HBRUSH hbrBack; /* brush for menu background */ HBRUSH hbrBack; /* brush for menu background */
DWORD dwContextHelpID; DWORD dwContextHelpID;
DWORD dwMenuData; /* application defined value */ DWORD dwMenuData; /* application defined value */
@ -141,6 +145,10 @@ typedef struct
/* Space between 2 columns */ /* Space between 2 columns */
#define MENU_COL_SPACE 4 #define MENU_COL_SPACE 4
/* top and bottom margins for popup menus */
#define MENU_TOP_MARGIN 3
#define MENU_BOTTOM_MARGIN 2
/* (other menu->FocusedItem values give the position of the focused item) */ /* (other menu->FocusedItem values give the position of the focused item) */
#define NO_SELECTED_ITEM 0xffff #define NO_SELECTED_ITEM 0xffff
@ -354,6 +362,50 @@ static HBITMAP get_arrow_bitmap(void)
return arrow_bitmap; return arrow_bitmap;
} }
/***********************************************************************
* get_down_arrow_bitmap
*/
static HBITMAP get_down_arrow_bitmap(void)
{
static HBITMAP arrow_bitmap;
if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
return arrow_bitmap;
}
/***********************************************************************
* get_down_arrow_inactive_bitmap
*/
static HBITMAP get_down_arrow_inactive_bitmap(void)
{
static HBITMAP arrow_bitmap;
if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
return arrow_bitmap;
}
/***********************************************************************
* get_up_arrow_bitmap
*/
static HBITMAP get_up_arrow_bitmap(void)
{
static HBITMAP arrow_bitmap;
if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
return arrow_bitmap;
}
/***********************************************************************
* get_up_arrow_inactive_bitmap
*/
static HBITMAP get_up_arrow_inactive_bitmap(void)
{
static HBITMAP arrow_bitmap;
if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
return arrow_bitmap;
}
/*********************************************************************** /***********************************************************************
* MENU_CopySysPopup * MENU_CopySysPopup
* *
@ -607,6 +659,28 @@ static void MENU_FreeItemData( MENUITEM* item )
HeapFree( GetProcessHeap(), 0, item->text ); HeapFree( GetProcessHeap(), 0, item->text );
} }
/***********************************************************************
* MENU_AdjustMenuItemRect
*
* Adjust menu item rectangle according to scrolling state.
*/
static void
MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
{
if (menu->bScrolling)
{
UINT arrow_bitmap_width, arrow_bitmap_height;
BITMAP bmp;
GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
arrow_bitmap_width = bmp.bmWidth;
arrow_bitmap_height = bmp.bmHeight;
rect->top += arrow_bitmap_height - menu->nScrollPos;
rect->bottom += arrow_bitmap_height - menu->nScrollPos;
}
}
/*********************************************************************** /***********************************************************************
* MENU_FindItemByCoords * MENU_FindItemByCoords
* *
@ -620,14 +694,17 @@ static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
MENUITEM *item; MENUITEM *item;
UINT i; UINT i;
RECT wrect; RECT wrect;
RECT rect;
if (!GetWindowRect(menu->hWnd,&wrect)) return NULL; if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
pt.x -= wrect.left;pt.y -= wrect.top; pt.x -= wrect.left;pt.y -= wrect.top;
item = menu->items; item = menu->items;
for (i = 0; i < menu->nItems; i++, item++) for (i = 0; i < menu->nItems; i++, item++)
{ {
if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) && rect = item->rect;
(pt.y >= item->rect.top) && (pt.y < item->rect.bottom)) MENU_AdjustMenuItemRect(menu, &rect);
if ((pt.x >= rect.left) && (pt.x < rect.right) &&
(pt.y >= rect.top) && (pt.y < rect.bottom))
{ {
if (pos) *pos = i; if (pos) *pos = i;
return item; return item;
@ -962,6 +1039,18 @@ static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
} }
/***********************************************************************
* MENU_GetMaxPopupHeight
*/
static UINT
MENU_GetMaxPopupHeight(LPPOPUPMENU lppop)
{
if (lppop->cyMax)
return lppop->cyMax;
return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
}
/*********************************************************************** /***********************************************************************
* MENU_PopupMenuCalcSize * MENU_PopupMenuCalcSize
* *
@ -972,7 +1061,7 @@ static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
MENUITEM *lpitem; MENUITEM *lpitem;
HDC hdc; HDC hdc;
int start, i; int start, i;
int orgX, orgY, maxX, maxTab, maxTabWidth; int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
lppop->Width = lppop->Height = 0; lppop->Width = lppop->Height = 0;
if (lppop->nItems == 0) return; if (lppop->nItems == 0) return;
@ -992,7 +1081,7 @@ static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
orgX = maxX; orgX = maxX;
if( lpitem->fType & MF_MENUBREAK) if( lpitem->fType & MF_MENUBREAK)
orgX += MENU_COL_SPACE; orgX += MENU_COL_SPACE;
orgY = 3; orgY = MENU_TOP_MARGIN;
maxTab = maxTabWidth = 0; maxTab = maxTabWidth = 0;
/* Parse items until column break or end of menu */ /* Parse items until column break or end of menu */
@ -1028,9 +1117,22 @@ static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
lppop->Width = maxX; lppop->Width = maxX;
/* space for 3d border */ /* space for 3d border */
lppop->Height += 2; lppop->Height += MENU_BOTTOM_MARGIN;
lppop->Width += 2; lppop->Width += 2;
/* Adjust popup height if it exceeds maximum */
maxHeight = MENU_GetMaxPopupHeight(lppop);
lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
if (lppop->Height >= maxHeight)
{
lppop->Height = maxHeight;
lppop->bScrolling = TRUE;
}
else
{
lppop->bScrolling = FALSE;
}
ReleaseDC( 0, hdc ); ReleaseDC( 0, hdc );
} }
@ -1109,6 +1211,52 @@ static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
} }
} }
/***********************************************************************
* MENU_DrawScrollArrows
*
* Draw scroll arrows.
*/
static void
MENU_DrawScrollArrows(LPPOPUPMENU lppop, HDC hdc)
{
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hOrigBitmap;
UINT arrow_bitmap_width, arrow_bitmap_height;
BITMAP bmp;
RECT rect;
GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
arrow_bitmap_width = bmp.bmWidth;
arrow_bitmap_height = bmp.bmHeight;
if (lppop->nScrollPos)
hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
else
hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
rect.left = 0;
rect.top = 0;
rect.right = lppop->Width;
rect.bottom = arrow_bitmap_height;
FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
rect.top = lppop->Height - arrow_bitmap_height;
rect.bottom = lppop->Height;
FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
SelectObject(hdcMem, get_down_arrow_bitmap());
else
SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
lppop->Height - arrow_bitmap_height,
arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hOrigBitmap);
DeleteDC(hdcMem);
}
/*********************************************************************** /***********************************************************************
* MENU_DrawMenuItem * MENU_DrawMenuItem
* *
@ -1182,6 +1330,7 @@ static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc,
dis.hwndItem = (HWND)hmenu; dis.hwndItem = (HWND)hmenu;
dis.hDC = hdc; dis.hDC = hdc;
dis.rcItem = lpitem->rect; dis.rcItem = lpitem->rect;
MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &dis.rcItem);
TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, " TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
"hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner, "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem, dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
@ -1195,6 +1344,7 @@ static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc,
if (menuBar && (lpitem->fType & MF_SEPARATOR)) return; if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
rect = lpitem->rect; rect = lpitem->rect;
MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
if (!(lpitem->fType & MF_OWNERDRAW)) if (!(lpitem->fType & MF_OWNERDRAW))
{ {
@ -1487,9 +1637,9 @@ static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
else else
DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT); DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
/* draw menu items */
menu = MENU_GetMenu( hmenu ); menu = MENU_GetMenu( hmenu );
/* draw menu items */
if (menu && menu->nItems) if (menu && menu->nItems)
{ {
MENUITEM *item; MENUITEM *item;
@ -1500,6 +1650,10 @@ static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
menu->Height, FALSE, ODA_DRAWENTIRE ); menu->Height, FALSE, ODA_DRAWENTIRE );
} }
/* draw scroll arrows */
if (menu->bScrolling)
MENU_DrawScrollArrows(menu, hdc);
} else } else
{ {
SelectObject( hdc, hPrevBrush ); SelectObject( hdc, hPrevBrush );
@ -1567,6 +1721,7 @@ static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
/* store the owner for DrawItem */ /* store the owner for DrawItem */
menu->hwndOwner = hwndOwner; menu->hwndOwner = hwndOwner;
menu->nScrollPos = 0;
MENU_PopupMenuCalcSize( menu, hwndOwner ); MENU_PopupMenuCalcSize( menu, hwndOwner );
/* adjust popup menu pos so that it fits within the desktop */ /* adjust popup menu pos so that it fits within the desktop */
@ -1609,6 +1764,47 @@ static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
} }
/***********************************************************************
* MENU_EnsureMenuItemVisible
*/
static void
MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
{
if (lppop->bScrolling)
{
MENUITEM *item = &lppop->items[wIndex];
UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
UINT nOldPos = lppop->nScrollPos;
RECT rc;
UINT arrow_bitmap_height;
BITMAP bmp;
GetClientRect(lppop->hWnd, &rc);
GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
arrow_bitmap_height = bmp.bmHeight;
rc.top += arrow_bitmap_height;
rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
{
lppop->nScrollPos = item->rect.bottom - nMaxHeight;
ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
MENU_DrawScrollArrows(lppop, hdc);
}
else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
{
lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
MENU_DrawScrollArrows(lppop, hdc);
}
}
}
/*********************************************************************** /***********************************************************************
* MENU_SelectItem * MENU_SelectItem
*/ */
@ -1645,6 +1841,7 @@ static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
{ {
if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) { if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
lppop->items[wIndex].fState |= MF_HILITE; lppop->items[wIndex].fState |= MF_HILITE;
MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc, MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
&lppop->items[wIndex], lppop->Height, &lppop->items[wIndex], lppop->Height,
!(lppop->wFlags & MF_POPUP), ODA_SELECT ); !(lppop->wFlags & MF_POPUP), ODA_SELECT );
@ -2074,10 +2271,13 @@ static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
GetWindowRect( menu->hWnd, &rect ); GetWindowRect( menu->hWnd, &rect );
if (menu->wFlags & MF_POPUP) if (menu->wFlags & MF_POPUP)
{ {
rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER); RECT rc = item->rect;
rect.top += item->rect.top;
rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER); MENU_AdjustMenuItemRect(menu, &rc);
rect.bottom = item->rect.top - item->rect.bottom; rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
rect.top += rc.top;
rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
rect.bottom = rc.top - rc.bottom;
} }
else else
{ {