📄 popupmenu.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
#include "PopupMenu.hpp"
#include "PopupMenuItem.hpp"
#include "MenuBar.hpp"
#include "Layout.hpp"
#include "PaintHelper.hpp"
#include "Debug.hpp"
#include "resource.h"
#include "Input.hpp"
/*------------------------------------------------------------------------------
PopupMenuImpl_t::PopupMenuImpl_t
Constructor
------------------------------------------------------------------------------*/
PopupMenuImpl_t::PopupMenuImpl_t(
)
{
TRACE(ZONE_COMMON_CTOR);
m_HideMyselfOnly = false;
m_IsInputMenu = false;
m_hMenu = NULL;
m_ParentPopup = NULL;
m_ChildPopup = NULL;
}
/*------------------------------------------------------------------------------
PopupMenuImpl_t::~PopupMenuImpl_t
Desstructor
------------------------------------------------------------------------------*/
PopupMenuImpl_t::~PopupMenuImpl_t(
)
{
TRACE(ZONE_COMMON_CTOR);
for (int i = 0; i < m_Items.size(); i++)
{
delete m_Items[i];
}
m_Items.clear();
}
/*------------------------------------------------------------------------------
PopupMenuImpl_t::ControlWindowProc
WndProc for this control
------------------------------------------------------------------------------*/
LRESULT
PopupMenuImpl_t::ControlWindowProc(
UINT Message,
WPARAM wParam,
LPARAM lParam,
bool& Handled
)
{
Handled = true;
switch (Message)
{
case WM_CREATE:
return OnCreate(reinterpret_cast<CREATESTRUCT*>(lParam));
case WM_ERASEBKGND:
//pretend to handle this...
return 1;
case WM_GETDLGCODE:
return DLGC_WANTALLKEYS;
case WM_KEYDOWN:
return OnKeydown(wParam, lParam);
case WM_KILLFOCUS:
return OnKillFocus(wParam, lParam);
case WM_LBUTTONDOWN:
return OnLButtonDown(wParam, lParam);
case WM_PAINT:
return OnPaint(reinterpret_cast<HDC>(wParam));
case WM_SETFOCUS:
return OnSetFocus(wParam, lParam);
case WM_COMMON_CALCULATE_DIMENSIONS:
return OnCalculateDimensions(reinterpret_cast<SIZE*>(lParam));
case WM_POPUPMENU_HIDEMENUS:
HidePopupMenus();
return 0;
case WM_POPUPMENU_INSERTITEM:
return OnInsertItem(reinterpret_cast<PopupMenuItem_t*>(lParam));
case WM_POPUPMENU_SETFLYOUTPARENT:
return OnSetFlyoutParent(reinterpret_cast<HWND>(lParam));
case WM_POPUPMENU_SETVISIBLEITEMS:
m_MaxNumberOfVisibleItems = wParam;
return 0;
default:
Handled = false;
return 0;
}
}
/*------------------------------------------------------------------------------
PopupMenuImpl_t::GetVisibleItems
Returns maximum number of visible items
------------------------------------------------------------------------------*/
int
PopupMenuImpl_t::GetVisibleItems(
void
)
{
if (m_MaxNumberOfVisibleItems == -1)
{
return m_Items.size();
}
return m_MaxNumberOfVisibleItems;
}
/*------------------------------------------------------------------------------
PopupMenuImpl_t::OnCreate
Handle WM_CREATE (the MenuBar Helper should be the create parameter)
------------------------------------------------------------------------------*/
LRESULT
PopupMenuImpl_t::OnCreate(
CREATESTRUCT* pCreateStruct
)
{
if (pCreateStruct)
{
m_MenuBar = (HWND)pCreateStruct->lpCreateParams;
m_IsInputMenu = (pCreateStruct->style & VPMS_INPUT) ? true : false;
}
m_TopItem = 0;
m_SelectedItem = 0;
m_MaxNumberOfVisibleItems = -1;
return 0;
}
/*------------------------------------------------------------------------------
PopupMenuImpl_t::OnInsertItem
Handle WM_POPUPMENU_INSERTITEM
------------------------------------------------------------------------------*/
LRESULT
PopupMenuImpl_t::OnInsertItem(
PopupMenuItem_t* pItem
)
{
if (! pItem)
{
ASSERT(FALSE);
return 0;
}
return (m_Items.push_back(pItem) ? 1 : 0);
}
/*------------------------------------------------------------------------------
PopupMenuImpl_t::OnCalculateDimensions
Calculate the dimensions of this object - for now
we have a static width
------------------------------------------------------------------------------*/
LRESULT
PopupMenuImpl_t::OnCalculateDimensions(
SIZE* pDimensions
)
{
if (! pDimensions)
{
ASSERT(FALSE);
return 0;
}
pDimensions->cx = 0;
for (int Index = 0; Index < m_Items.size(); Index++)
{
RECT ItemRect = {0};
PaintHelper_t::CalculateTextDimensions(
m_Items[Index]->GetText(),
-1,
Fonts_t::StandardText(),
&ItemRect,
DT_LEFT | DT_SINGLELINE
);
pDimensions->cx = max(pDimensions->cx, ItemRect.right);
}
HRESULT hr;
Bitmap_t CascadeArrow;
hr = CascadeArrow.Attach(
GlobalData_t::s_GDICacheObject.LoadCachedBitmap(IDB_POPUPMENU_RIGHT_ARROW)
);
ASSERT(SUCCEEDED(hr));
pDimensions->cx += 2*Layout_t::PopupTextMargin() +
CascadeArrow.Width() +
Layout_t::PopupRightArrowRightMargin();
if (Layout_t::PopupItemWidth() > pDimensions->cx)
{
pDimensions->cx = Layout_t::PopupItemWidth();
}
pDimensions->cy = GetVisibleItems() * Layout_t::PopupItemHeight();
return 1;
}
/*------------------------------------------------------------------------------
PopupMenuImpl_t::OnSetFocus
Handle us gaining focus
------------------------------------------------------------------------------*/
LRESULT
PopupMenuImpl_t::OnSetFocus(
WPARAM wParam,
LPARAM lParam
)
{
//set a default selection
if (m_SelectedItem == -1)
{
m_SelectedItem = m_TopItem;
}
//reset popup hierarchy
m_ChildPopup = (HWND)NULL;
m_HideMyselfOnly = false;
return DefWindowProc(WM_SETFOCUS, wParam, lParam);
}
/*------------------------------------------------------------------------------
PopupMenuImpl_t::OnKillFocus
Handle us losing focus. If we lose focus unintentionally, we should hide our window
------------------------------------------------------------------------------*/
LRESULT
PopupMenuImpl_t::OnKillFocus(
WPARAM wParam,
LPARAM lParam
)
{
LRESULT Result = DefWindowProc(WM_KILLFOCUS, wParam, lParam);
HWND NewFocus = reinterpret_cast<HWND>(wParam);
//if we lost focus to our child window - or are just hiding ourself, we don't
//need to hide our parent as well
if (NewFocus && (NewFocus == m_ChildPopup || m_HideMyselfOnly))
{
return Result;
}
//otherwise, we need to hide and inform our parent of why we are hiding
HidePopupMenus();
return Result;
}
/*------------------------------------------------------------------------------
PopupMenuImpl_t::OnLButtonDown
Handle a mouse click in our client area
------------------------------------------------------------------------------*/
LRESULT
PopupMenuImpl_t::OnLButtonDown(
WPARAM wParam,
LPARAM lParam
)
{
//give ourself focus back!
SetFocus(m_hwnd);
WORD yPos = HIWORD(lParam);
//based on the y-position, figure out the index of the item that was
//pressed on
int IndexPressed = m_TopItem + yPos/Layout_t::PopupItemHeight();
//set the pressed item to be the selected item
if (! SetCurrentSelection(IndexPressed))
{
return 0;
}
//force a quick redraw (and sleep for a tiny bit, so the user sees an update)
UpdateWindow(m_hwnd);
Sleep(150);
OnItemActivated();
return 0;
}
/*------------------------------------------------------------------------------
PopupMenuImpl_t::OnSetFlyoutParent
Handle WM_POPUPMENU_SETFLYOUTPARENT
------------------------------------------------------------------------------*/
LRESULT
PopupMenuImpl_t::OnSetFlyoutParent(
HWND FlyoutParent
)
{
m_ParentPopup = FlyoutParent;
return 0;
}
/*------------------------------------------------------------------------------
PopupMenuImpl_t::SetCurrentSelection
Set a new current selection and redraw part of the window
------------------------------------------------------------------------------*/
bool
PopupMenuImpl_t::SetCurrentSelection(
int NewSelection
)
{
//validate the new selection
if (NewSelection < 0 || NewSelection > m_Items.size())
{
return false;
}
//has the selection changed?
if (NewSelection == m_SelectedItem)
{
return true;
}
int MaxVisibleIndex = GetVisibleItems() - 1;
bool NeedToUpdateTopItem = false;
if (NewSelection < m_TopItem)
{
NeedToUpdateTopItem = true;
m_TopItem = NewSelection;
}
else if (NewSelection > (m_TopItem + MaxVisibleIndex))
{
NeedToUpdateTopItem = true;
m_TopItem = NewSelection - MaxVisibleIndex;
}
if (NeedToUpdateTopItem)
{
m_SelectedItem = NewSelection;
InvalidateRect(m_hwnd, NULL, TRUE);
return true;
}
//invalidate the rects of the old item and new item
RECT ItemRect = {0};
GetItemRect(NewSelection, &ItemRect);
InvalidateRect(m_hwnd, &ItemRect, TRUE);
GetItemRect(m_SelectedItem, &ItemRect);
InvalidateRect(m_hwnd, &ItemRect, TRUE);
m_SelectedItem = NewSelection;
return true;
}
/*------------------------------------------------------------------------------
PopupMenuImpl_t::GetItemRect
Get the drawing rectangle for a specific item
------------------------------------------------------------------------------*/
void
PopupMenuImpl_t::GetItemRect(
int Index,
RECT* pRect
)
{
ASSERT(pRect);
GetClientRect(m_hwnd, pRect);
// Adjust index to be based on top item
Index -= m_TopItem;
pRect->top = Index*Layout_t::PopupItemHeight();
pRect->bottom = (Index+1)*Layout_t::PopupItemHeight() - 1;
return;
}
/*------------------------------------------------------------------------------
PopupMenuImpl_t::OnKeydown
Handle a WM_KEYDOWN event while we have keyboard focus
------------------------------------------------------------------------------*/
LRESULT
PopupMenuImpl_t::OnKeydown(
WPARAM wParam,
LPARAM lParam
)
{
int Keycode = static_cast<int>(wParam);
switch (Keycode)
{
case VK_UP:
if (m_SelectedItem)
{
SetCurrentSelection(m_SelectedItem - 1);
}
else
{
SetCurrentSelection(m_Items.size() - 1);
}
break;
case VK_DOWN:
if (m_SelectedItem == (m_Items.size()-1))
{
SetCurrentSelection(0);
}
else
{
SetCurrentSelection(m_SelectedItem + 1);
}
break;
case VK_LEFT:
HideMyselfOnly();
break;
case VK_RIGHT:
//right means dig in deeper (if the selected item has a popup menu)
if (m_SelectedItem >= 0 && m_SelectedItem < m_Items.size())
{
if (m_Items[m_SelectedItem]->GetPopupMenu() != NULL)
{
OnItemActivated();
}
}
break;
case VK_RETURN:
OnItemActivated();
break;
default:
break;
}
return 0;
}
/*------------------------------------------------------------------------------
PopupMenuImpl_t::HideMyselfOnly
Hide ourself only
------------------------------------------------------------------------------*/
void
PopupMenuImpl_t::HideMyselfOnly(
)
{
m_HideMyselfOnly = true;
ShowWindow(m_hwnd, SW_HIDE);
m_TopItem = 0;
m_SelectedItem = -1;
if (IsWindow((HWND)m_ParentPopup))
{
SetFocus((HWND)m_ParentPopup);
}
return;
}
/*------------------------------------------------------------------------------
PopupMenuImpl_t::HidePopupMenus
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -