2025-11-28 00:35:46 +09:00

345 lines
9.2 KiB
C++

//------------------------------------------------------------------------------
//
// File: ThemedButton.cpp
// Description: Implements a theme-aware button control.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//------------------------------------------------------------------------------
#include "MFPlayer.h"
BOOL AlphaDrawBitmap(HDC hdc, const RECT& rcDest, BitmapStrip& bitmap, UINT index);
//------------------------------------------------------------------------------
// Load
// Loads the bitmap that contains the image strip.
//
// nID: Resource ID of the bitmap
// cImages: Count of images in the bitmap.
//------------------------------------------------------------------------------
BOOL BitmapStrip::Load(int nID, int cImages)
{
if (m_hBitmap)
{
DeleteObject(m_hBitmap);
}
m_cImages = 0;
m_hBitmap = LoadBitmap(GetInstance(), MAKEINTRESOURCE(nID));
if (m_hBitmap == NULL)
{
return FALSE;
}
BITMAP bm;
ZeroMemory(&bm, sizeof(bm));
GetObject(m_hBitmap, sizeof(BITMAP), &bm);
// Calculate the size of each image.
if (cImages == 0)
{
cImages = 1;
}
m_size.cx = bm.bmWidth / cImages;
m_size.cy = bm.bmHeight;
m_cImages = cImages;
return TRUE;
}
//------------------------------------------------------------------------------
// GetImage
//
// Returns the following information for one image in the list:
// - Handle to the bitmap
// - Subrectangle within the bitmap that contains the image.
//
// i: Index of the image to get information about.
//------------------------------------------------------------------------------
BOOL BitmapStrip::GetImage(UINT i, BitmapStripInfo *pInfo)
{
if (i >= m_cImages)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
SetRect(&pInfo->rc, m_size.cx * i, 0, m_size.cx * (i + 1), m_size.cy);
pInfo->hBitmap = m_hBitmap;
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////////
ThemedButton::ThemedButton() : m_hwnd(NULL), m_hTheme(NULL), m_bThemesActive(TRUE)
{
ZeroMemory(&m_stateMap, sizeof(m_stateMap));
}
ThemedButton::~ThemedButton()
{
CloseTheme();
}
//------------------------------------------------------------------------------
// OpenTheme
//
// Opens the theme handle.
//------------------------------------------------------------------------------
void ThemedButton::OpenTheme()
{
if (m_hTheme == NULL && m_bThemesActive)
{
if (IsThemeActive())
{
m_hTheme = OpenThemeData(m_hwnd, L"Button");
}
else
{
m_bThemesActive = FALSE;
}
}
}
//------------------------------------------------------------------------------
// CloseTheme
//
// Closes the theme handle.
//------------------------------------------------------------------------------
void ThemedButton::CloseTheme()
{
if (m_hTheme)
{
CloseThemeData(m_hTheme);
m_hTheme = NULL;
}
}
//------------------------------------------------------------------------------
// ResetTheme
// Resets the theme.
//
// Call this method in response to WM_THEMECHANGED messages.
//------------------------------------------------------------------------------
void ThemedButton::ResetTheme()
{
CloseTheme();
m_bThemesActive = TRUE;
OpenTheme();
}
//------------------------------------------------------------------------------
// LoadBitmap
// Loads the bitmap that contains the image strip.
//
// nID: Resource ID of the bitmap
// cImages: Count of images in the bitmap.
//------------------------------------------------------------------------------
BOOL ThemedButton::LoadBitmap(int nID, int cImages)
{
// Clear the mapping of states to images.
ZeroMemory(&m_stateMap, sizeof(m_stateMap));
BOOL bResult = m_bitmap.Load(nID, cImages);
if (bResult)
{
// By default, assume that the image strip is ordered by button state.
// In other words, for button_state == i, use image[i]
DWORD count = min(cImages,NUM_THEME_STATES);
for (DWORD i = 0; i < count; i++)
{
m_stateMap[i] = i;
}
}
return bResult;
}
//------------------------------------------------------------------------------
// SetButtonImage
// Associate button state "iState" with image "iImageIndex"
//
// In other words, when the button state equals iState, the button draws the
// image located at index = iImageIndex in the image strip.
//
// The default ordering is iState == iImageIndex (see LoadBitmap)
//------------------------------------------------------------------------------
BOOL ThemedButton::SetButtonImage(UINT iState, UINT iImageIndex)
{
if (iImageIndex >= m_bitmap.NumImages())
{
return FALSE;
}
if (iState == (UINT)-1)
{
for (DWORD i = 0; i < NUM_THEME_STATES; i++)
{
m_stateMap[i] = iImageIndex;
}
return TRUE;
}
if (iState < 1 || iState > NUM_THEME_STATES)
{
return FALSE;
}
m_stateMap[iState] = iImageIndex;
return TRUE;
}
//------------------------------------------------------------------------------
// Draw
// Draws the themed button.
//
// Call this method in response to NM_CUSTOMDRAW messages.
//------------------------------------------------------------------------------
LRESULT ThemedButton::Draw(const NMCUSTOMDRAW *pDraw)
{
OpenTheme();
int iPartId = BP_PUSHBUTTON;
int iStateId = PBS_NORMAL;
// Translate the button state into the theme state.
if (HasStyle(WS_DISABLED))
{
iStateId = PBS_DISABLED;
}
else if (pDraw->uItemState & CDIS_SELECTED)
{
iStateId = PBS_PRESSED; // Button is pressed.
}
else if (pDraw->uItemState & CDIS_HOT)
{
iStateId = PBS_HOT; // Button is hot but not pressed.
}
else if (HasStyle(BS_DEFPUSHBUTTON))
{
iStateId = PBS_DEFAULTED; // Default button, but not hot or pressed.
}
// Note: This class does not support PBS_DEFAULTED_ANIMATING.
// Now draw the different parts.
RECT rcClient; // Entire client rect for the button.
RECT rcContent; // Content rect is a subrect of the client rect, when themed.
GetClientRect(m_hwnd, &rcClient);
if ((HTHEME)m_hTheme != NULL)
{
// Themes are active. Draw the background using themes.
if (IsThemeBackgroundPartiallyTransparent(m_hTheme, iPartId, iStateId))
{
DrawThemeParentBackground(m_hwnd, pDraw->hdc, &pDraw->rc);
}
DrawThemeBackground(m_hTheme, pDraw->hdc, iPartId, iStateId, &pDraw->rc, &pDraw->rc);
// Find the content rect.
GetThemeBackgroundContentRect(m_hTheme, pDraw->hdc, iPartId, iStateId, &rcClient, &rcContent);
}
else
{
// Themes are not active. Draw the normal button color.
FillRect(pDraw->hdc, &rcClient, (HBRUSH)(COLOR_BTNFACE+1));
rcContent = rcClient;
}
// Draw the bitmap image.
AlphaDrawBitmap(pDraw->hdc, rcContent, m_bitmap, m_stateMap[iStateId]);
// This return value tells Windows not to draw any part of the button.
return CDRF_SKIPDEFAULT;
}
//------------------------------------------------------------------------------
// AlphaDrawBitmap
// Alpha blends an image from a BitmapStrip to a DC.
//
// hdc: Device context
// rcDest: Destination rectangle
// bitmap: Bitmap strip
// index: Index of the image in the strip.
//------------------------------------------------------------------------------
BOOL AlphaDrawBitmap(HDC hdc, const RECT& rcDest, BitmapStrip& bitmap, UINT index)
{
BitmapStripInfo info;
BOOL bResult = bitmap.GetImage(index, &info);
if (!bResult)
{
return FALSE;
}
HDC hdcMem = CreateCompatibleDC(hdc);
if (hdcMem == NULL)
{
return FALSE;
}
SelectObject(hdcMem, info.hBitmap);
BLENDFUNCTION BlendFn;
ZeroMemory(&BlendFn, sizeof(&BlendFn));
BlendFn.BlendOp = AC_SRC_OVER;
BlendFn.SourceConstantAlpha = 0xFF;
BlendFn.AlphaFormat = AC_SRC_ALPHA;
LONG src_w = Width(info.rc);
LONG src_h = Height(info.rc);
LONG dest_w = Width(rcDest);
LONG dest_h = Height(rcDest);
LONG padX = max(0, dest_w - src_w) / 2;
LONG padY = max(0, dest_h - src_h) / 2;
LONG width = min(src_w, src_w);
LONG height = min(dest_h, src_h);
bResult = AlphaBlend(
hdc, // handle to destination DC
rcDest.left + padX, // x-coord of upper-left corner
rcDest.top + padY, // y-coord of upper-left corner
width, // destination width
height, // destination height
hdcMem, // handle to source DC
info.rc.left, // x-coord of upper-left corner
info.rc.top, // y-coord of upper-left corner
src_w, // source width
src_h, // source height
BlendFn // alpha-blending function
);
DeleteDC(hdcMem);
return TRUE;
}