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

828 lines
24 KiB
C++

// 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 <windows.h>
#include <vssym32.h>
#include <Uxtheme.h>
#include <dwmapi.h>
#pragma comment(lib, "Uxtheme.lib")
#pragma comment(lib, "dwmapi.lib")
#pragma comment(linker, "\"/manifestdependency:type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
//
// Overview
//
// Normally, pop-up windows receive activation, resulting in the
// owner window being de-activated. To prevent the owner from
// being de-activated, the pop-up window should not receive
// activation.
//
// Since the pop-up window is not active, input messages are not
// delivered to the pop-up. Instead, the input messages must be
// explicitly inspected by the message loop.
//
// Our sample program illustrates how you can create a pop-up
// window that contains a selection of colors.
//
// Right-click in the window to change its background color
// via the fake menu popup. Observe
//
// - The caption of the main application window remains
// highlighted even though the fake-menu is "active".
//
// - The current fake-menu item highlight follows the mouse.
//
// - The keyboard arrows can be used to move the highlight,
// ESC cancels the fake-menu, Enter accepts the fake-menu.
//
// - The fake-menu appears on the correct monitor (for
// multiple-monitor systems).
//
// - The fake-menu switches to keyboard focus highlighting
// once keyboard menu navigation is employed.
HINSTANCE g_hInstance = NULL;
COLORREF g_clrBackground; // The selected color
// This is the array of predefined colors we put into the color picker.
const COLORREF c_rgclrPredef[] =
{
RGB(0x00, 0x00, 0x00), // 0 = black
RGB(0x80, 0x00, 0x00), // 1 = maroon
RGB(0x00, 0x80, 0x00), // 2 = green
RGB(0x80, 0x80, 0x00), // 3 = olive
RGB(0x00, 0x00, 0x80), // 4 = navy
RGB(0x80, 0x00, 0x80), // 5 = purple
RGB(0x00, 0x80, 0x80), // 6 = teal
RGB(0x80, 0x80, 0x80), // 7 = gray
RGB(0xC0, 0xC0, 0xC0), // 8 = silver
RGB(0xFF, 0x00, 0x00), // 9 = red
RGB(0x00, 0xFF, 0x00), // A = lime
RGB(0xFF, 0xFF, 0x00), // B = yellow
RGB(0x00, 0x00, 0xFF), // C = blue
RGB(0xFF, 0x00, 0xFF), // D = fuchsia
RGB(0x00, 0xFF, 0xFF), // E = cyan
RGB(0xFF, 0xFF, 0xFF), // F = white
};
// FillRectClr
//
// Helper function to fill a rectangle with a solid color.
//
void FillRectClr(HDC hdc, LPCRECT prc, COLORREF clr)
{
SetDCBrushColor(hdc, clr);
FillRect(hdc, prc, (HBRUSH)GetStockObject(DC_BRUSH));
}
bool IsHighContrast()
{
HIGHCONTRAST hc = { 0 };
hc.cbSize = sizeof(hc);
hc.dwFlags = 0;
SystemParametersInfo(SPI_GETHIGHCONTRAST, sizeof(HIGHCONTRAST), &hc, 0);
return hc.dwFlags & HCF_HIGHCONTRASTON;
}
//
// COLORPICKSTATE
//
// Structure that records the state of a color-picker pop-up.
//
// A pointer to this state information is kept in the GWLP_USERDATA
// window long.
//
// The iSel field is the index of the selected color, or the
// special value -1 to mean that no item is highlighted.
//
typedef struct COLORPICKSTATE
{
BOOL fDone; // Set when we should get out
int iSel; // Which color is selected?
int iResult; // Which color should be returned?
HWND hwndOwner; // Our owner window
BOOL fKeyboardUsed; // Has the keyboard been used?
HTHEME hTheme; // The active theme, if any
int iPartKeyboardFocus; // MENU_POPUPITEMKBFOCUS if supported, else MENU_POPUPITEM
int iPartNonFocus; // MENU_POPUPITEM_FOCUSABLE if supported, else MENU_POPUPITEM
int iStateFocus; // MPIF_HOT if using the FOCUSABLE parts, else MPI_HOT
MARGINS marginsItem; // Margins of a popup item
} COLORPICKSTATE;
void ColorPickState_Initialize(COLORPICKSTATE* pcps, HWND hwndOwner)
{
pcps->fDone = FALSE; // Not done yet
pcps->iSel = -1; // No initial selection
pcps->iResult = -1; // No result
pcps->hwndOwner = hwndOwner; // Owner window
pcps->fKeyboardUsed = FALSE; // No keyboard usage yet
}
#define CYCOLOR 16 // Height of a single color pick
#define CXFAKEMENU 100 // Width of our fake menu
//
// ColorPick_GetColorRect
//
// Returns the rectangle that encloses the specified color.
//
void ColorPick_GetColorRect(RECT *prc, int iColor)
{
// Build the "menu" item rect.
prc->left = 0;
prc->right = CXFAKEMENU;
prc->top = iColor * CYCOLOR;
prc->bottom = prc->top + CYCOLOR;
}
// ColorPick_UpdateVisuals
//
// Update the theme and nonclient rendering to match current user preference.
//
void ColorPick_UpdateVisuals(COLORPICKSTATE* pcps, HWND hwnd)
{
if (pcps->hTheme)
{
CloseThemeData(pcps->hTheme);
pcps->hTheme = NULL;
}
if (IsThemeActive() && !IsHighContrast())
{
pcps->hTheme = OpenThemeData(hwnd, VSCLASS_MENU);
}
if (pcps->hTheme)
{
#if defined(NTDDI_WIN10_CO) && (NTDDI_VERSION >= NTDDI_WIN10_CO)
DWM_WINDOW_CORNER_PREFERENCE preference = DWMWCP_ROUNDSMALL;
DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &preference, sizeof(preference));
COLORREF color;
if (SUCCEEDED(GetThemeColor(pcps->hTheme, MENU_POPUPBORDERS, 0, TMT_FILLCOLORHINT, &color)))
{
DwmSetWindowAttribute(hwnd, DWMWA_BORDER_COLOR, &color, sizeof(color));
}
#endif
}
else
{
#if defined(NTDDI_WIN10_CO) && (NTDDI_VERSION >= NTDDI_WIN10_CO)
DWM_WINDOW_CORNER_PREFERENCE preference = DWMWCP_DEFAULT;
DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &preference, sizeof(preference));
COLORREF color = DWMWA_COLOR_DEFAULT;
DwmSetWindowAttribute(hwnd, DWMWA_BORDER_COLOR, &color, sizeof(color));
#endif
}
#if defined(NTDDI_WIN10_NI) && (NTDDI_VERSION >= NTDDI_WIN10_NI)
// Use the MENU_POPUPITEMKBFOCUS and MENU_POPUPITEM_FOCUSABLE parts if available.
if (pcps->hTheme &&
IsThemePartDefined(pcps->hTheme, MENU_POPUPITEM_FOCUSABLE, MPIF_HOT))
{
pcps->iPartKeyboardFocus = MENU_POPUPITEMKBFOCUS;
pcps->iPartNonFocus = MENU_POPUPITEM_FOCUSABLE;
pcps->iStateFocus = MPIF_HOT;
}
else
#endif
{
pcps->iPartKeyboardFocus = MENU_POPUPITEM;
pcps->iPartNonFocus = MENU_POPUPITEM;
pcps->iStateFocus = MPI_HOT;
}
if (pcps->hTheme &&
SUCCEEDED(GetThemeMargins(pcps->hTheme, NULL, MENU_POPUPITEM, 0, TMT_CONTENTMARGINS, NULL, &pcps->marginsItem)))
{
// Successfully obtained item content margins.
}
else
{
// Use fallback values for item content margins.
pcps->marginsItem.cxLeftWidth = pcps->marginsItem.cxRightWidth = GetSystemMetrics(SM_CXEDGE);
pcps->marginsItem.cyTopHeight = pcps->marginsItem.cyBottomHeight = GetSystemMetrics(SM_CYEDGE);
}
// Force everything to repaint with the new visuals.
InvalidateRect(hwnd, NULL, TRUE);
}
// ColorPick_OnCreate
//
// Stash away our state.
//
LRESULT ColorPick_OnCreate(HWND hwnd, LPCREATESTRUCT pcs)
{
auto pcps = reinterpret_cast<COLORPICKSTATE*>(pcs->lpCreateParams);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pcps);
ColorPick_UpdateVisuals(pcps, hwnd);
return 0;
}
//
// ColorPick_OnPaint
//
// Draw the background, color bars, and appropriate highlighting for the selected color.
//
void ColorPick_OnPaint(COLORPICKSTATE* pcps, HWND hwnd)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
if (hdc)
{
RECT rcClient;
GetClientRect(hwnd, &rcClient);
// Let the theme chooses the background fill color.
if (pcps->hTheme)
{
COLORREF color;
if (SUCCEEDED(GetThemeColor(pcps->hTheme, MENU_POPUPBACKGROUND, 0, TMT_FILLCOLORHINT, &color)))
{
FillRectClr(hdc, &ps.rcPaint, color);
}
}
// For each of our predefined colors, draw it in a little
// rectangular region, leaving some border so the user can
// see if the item is highlighted or not.
for (int iColor = 0; iColor < ARRAYSIZE(c_rgclrPredef); iColor++)
{
// Build the "menu" item rect.
RECT rc;
ColorPick_GetColorRect(&rc, iColor);
// If the item is highlighted, then draw a highlighted background.
if (iColor == pcps->iSel)
{
// Draw focus effect.
if (pcps->hTheme)
{
// If keyboard navigation active, then use the keyboard focus visuals.
// Otherwise, use the traditional visuals.
DrawThemeBackground(pcps->hTheme, hdc,
pcps->fKeyboardUsed ? pcps->iPartKeyboardFocus : MENU_POPUPITEM,
pcps->fKeyboardUsed ? pcps->iStateFocus : MPI_HOT, &rc, nullptr);
}
else
{
FillRect(hdc, &rc, GetSysColorBrush(COLOR_HIGHLIGHT));
}
}
else
{
// Draw non-focus effect.
if (pcps->hTheme)
{
DrawThemeBackground(pcps->hTheme, hdc,
pcps->fKeyboardUsed ? pcps->iPartNonFocus : MENU_POPUPITEM,
MPI_NORMAL, &rc, nullptr);
}
else
{
// If not themed, then our background brush already filled for us.
}
}
// Now shrink the rectangle by the margins and fill the rest with the
// color of the item itself.
rc.left += pcps->marginsItem.cxLeftWidth;
rc.right -= pcps->marginsItem.cxRightWidth;
rc.top += pcps->marginsItem.cyTopHeight;
rc.bottom -= pcps->marginsItem.cyBottomHeight;
FillRectClr(hdc, &rc, c_rgclrPredef[iColor]);
}
EndPaint(hwnd, &ps);
}
}
//
// ColorPick_ChangeSel
//
// Change the selection to the specified item.
//
void ColorPick_ChangeSel(COLORPICKSTATE* pcps, HWND hwnd, int iSel)
{
// If the selection changed, then repaint the items that need repainting.
if (pcps->iSel != iSel)
{
if (pcps->iSel >= 0)
{
RECT rc;
ColorPick_GetColorRect(&rc, pcps->iSel);
InvalidateRect(hwnd, &rc, TRUE);
}
pcps->iSel = iSel;
if (pcps->iSel >= 0)
{
RECT rc;
ColorPick_GetColorRect(&rc, pcps->iSel);
InvalidateRect(hwnd, &rc, TRUE);
}
}
}
//
// ColorPick_OnMouseMove
//
// Track the mouse to see if it is over any of our colors.
//
void ColorPick_OnMouseMove(COLORPICKSTATE* pcps, HWND hwnd, int x, int y)
{
int iSel;
if (x >= 0 && x < CXFAKEMENU &&
y >= 0 && y < ARRAYSIZE(c_rgclrPredef) * CYCOLOR)
{
iSel = y / CYCOLOR;
}
else
{
iSel = -1;
}
ColorPick_ChangeSel(pcps, hwnd, iSel);
}
//
// ColorPick_OnLButtonUp
//
// When the button comes up, we are done.
//
void ColorPick_OnLButtonUp(COLORPICKSTATE* pcps, HWND hwnd, int x, int y)
{
// First track to the final location, in case the user moves the mouse
// REALLY FAST and immediately lets go.
ColorPick_OnMouseMove(pcps, hwnd, x, y);
// Set the result to the current selection.
pcps->iResult = pcps->iSel;
// And tell the message loop that we're done.
pcps->fDone = TRUE;
}
//
// ColorPick_OnKeyDown
//
// If the ESC key is pressed, then abandon the fake menu.
// If the Enter key is pressed, then accept the current selection.
// If an arrow key is pressed, the move the selection.
//
void ColorPick_OnKeyDown(COLORPICKSTATE* pcps, HWND hwnd, WPARAM vk)
{
pcps->fKeyboardUsed = TRUE;
switch (vk)
{
case VK_ESCAPE:
pcps->fDone = TRUE; // Abandoned
break;
case VK_RETURN:
pcps->iResult = pcps->iSel; // Accept current selection
pcps->fDone = TRUE;
break;
case VK_UP:
if (pcps->iSel > 0) // Decrement selection
{
ColorPick_ChangeSel(pcps, hwnd, pcps->iSel - 1);
}
else
{
ColorPick_ChangeSel(pcps, hwnd, ARRAYSIZE(c_rgclrPredef) - 1);
}
break;
case VK_DOWN: // Increment selection
if (pcps->iSel + 1 < ARRAYSIZE(c_rgclrPredef))
{
ColorPick_ChangeSel(pcps, hwnd, pcps->iSel + 1);
}
else
{
ColorPick_ChangeSel(pcps, hwnd, 0);
}
break;
}
}
//
// ColorPick_WndProc
//
// Window procedure for the color picker popup.
//
LRESULT CALLBACK ColorPick_WndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
COLORPICKSTATE* pcps = (COLORPICKSTATE*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
switch (uiMsg)
{
case WM_CREATE:
return ColorPick_OnCreate(hwnd, (LPCREATESTRUCT)lParam);
case WM_MOUSEMOVE:
ColorPick_OnMouseMove(pcps, hwnd, (short)LOWORD(lParam),
(short)HIWORD(lParam));
break;
case WM_LBUTTONUP:
ColorPick_OnLButtonUp(pcps, hwnd, (short)LOWORD(lParam),
(short)HIWORD(lParam));
break;
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
ColorPick_OnKeyDown(pcps, hwnd, wParam);
break;
// Do not activate when somebody clicks the window.
case WM_MOUSEACTIVATE:
return MA_NOACTIVATE;
case WM_PAINT:
ColorPick_OnPaint(pcps, hwnd);
return 0;
case WM_THEMECHANGED:
case WM_SETTINGCHANGE:
ColorPick_UpdateVisuals(pcps, hwnd);
break;
}
return DefWindowProc(hwnd, uiMsg, wParam, lParam);
}
//
// ColorPick_ChooseLocation
//
// Find a place to put the window so it won't go off the screen
// or straddle two monitors.
//
// x, y = location of mouse click (preferred upper-left corner)
// cx, cy = size of window being created
//
// We use the same logic that real menus use.
//
// - If (x, y) is too high or too far left, then slide onto screen.
// - Use (x, y) if all fits on the monitor.
// - If too low, then slide up.
// - If too far right, then flip left.
//
void ColorPick_ChooseLocation(HWND hwnd, int x, int y, int cx, int cy, LPPOINT ppt)
{
// First get the dimensions of the monitor that contains (x, y).
ppt->x = x;
ppt->y = y;
HMONITOR hmon = MonitorFromPoint(*ppt, MONITOR_DEFAULTTONULL);
// If (x, y) is not on any monitor, then use the monitor that the owner
// window is on.
if (hmon == NULL)
{
hmon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
}
MONITORINFO minf;
minf.cbSize = sizeof(minf);
GetMonitorInfo(hmon, &minf);
// Now slide things around until they fit.
// If too high, then slide down.
if (ppt->y < minf.rcMonitor.top)
{
ppt->y = minf.rcMonitor.top;
}
// If too far left, then slide right.
if (ppt->x < minf.rcMonitor.left)
{
ppt->x = minf.rcMonitor.left;
}
// If too low, then slide up.
if (ppt->y > minf.rcMonitor.bottom - cy)
{
ppt->y = minf.rcMonitor.bottom - cy;
}
// If too far right, then flip left.
if (ppt->x > minf.rcMonitor.right - cx)
{
ppt->x = ppt->x - cx;
}
}
//
// ColorPick_Popup
//
// Display a fake menu to allow the user to select the color.
//
// Return the color index the user selected, or -1 if no color
// was selected.
//
int ColorPick_Popup(HWND hwndOwner, int x, int y)
{
// Early check: We must be on same thread as the owner so we can see
// its mouse and keyboard messages when we set capture to it.
if (GetCurrentThreadId() != GetWindowThreadProcessId(hwndOwner, NULL))
{
// Error: Menu must be on same thread as parent window.
return -1;
}
COLORPICKSTATE cps;
ColorPickState_Initialize(&cps, hwndOwner);
// Set up the style and extended style we want to use.
DWORD dwStyle = WS_POPUP | WS_BORDER;
DWORD dwExStyle = WS_EX_TOOLWINDOW | // So it doesn't show up in taskbar
WS_EX_DLGMODALFRAME | // Get the edges right
WS_EX_WINDOWEDGE |
WS_EX_TOPMOST; // So it isn't obscured
// We want a client area of size (CXFAKEMENU, ARRAYSIZE(c_rgclrPredef) * CYCOLOR),
// so use AdjustWindowRectEx to figure out what window rect will give us a
// client rect of that size.
RECT rc;
rc.left = 0;
rc.top = 0;
rc.right = CXFAKEMENU;
rc.bottom = ARRAYSIZE(c_rgclrPredef) * CYCOLOR;
AdjustWindowRectEx(&rc, dwStyle, FALSE, dwExStyle);
// Now find a proper home for the window that won't go off the screen or
// straddle two monitors.
int cx = rc.right - rc.left;
int cy = rc.bottom - rc.top;
POINT pt;
ColorPick_ChooseLocation(hwndOwner, x, y, cx, cy, &pt);
HWND hwndPopup = CreateWindowEx(
dwExStyle,
L"ColorPick",
L"",
dwStyle,
pt.x, pt.y,
cx, cy,
hwndOwner,
NULL,
g_hInstance,
&cps);
// Show the window but don't activate it!
ShowWindow(hwndPopup, SW_SHOWNOACTIVATE);
// We want to receive all mouse messages, but since only the active
// window can capture the mouse, we have to set the capture to our
// owner window, and then steal the mouse messages out from under it.
SetCapture(hwndOwner);
// Go into a message loop that filters all the messages it receives
// and route the interesting ones to the color picker window.
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
// Something may have happened that caused us to stop.
if (cps.fDone)
{
break;
}
// If our owner stopped being the active window (e.g. the user
// Alt+Tab'd to another window in the meantime), then stop.
HWND hwndActive = GetActiveWindow();
if (hwndActive != hwndOwner && !IsChild(hwndActive, hwndOwner) ||
GetCapture() != hwndOwner)
{
break;
}
// At this point, we get to snoop at all input messages before
// they get dispatched. This allows us to route all input to our
// popup window even if really belongs to somebody else.
// All mouse messages are remunged and directed at our popup
// menu. If the mouse message arrives as client coordinates, then
// we have to convert it from the client coordinates of the original
// target to the client coordinates of the new target.
switch (msg.message)
{
// These mouse messages arrive in client coordinates, so in
// addition to stealing the message, we also need to convert the
// coordinates.
case WM_MOUSEMOVE:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MBUTTONDBLCLK:
pt.x = (short)LOWORD(msg.lParam);
pt.y = (short)HIWORD(msg.lParam);
MapWindowPoints(msg.hwnd, hwndPopup, &pt, 1);
msg.lParam = MAKELPARAM(pt.x, pt.y);
msg.hwnd = hwndPopup;
break;
// These mouse messages arrive in screen coordinates, so we just
// need to steal the message.
case WM_NCMOUSEMOVE:
case WM_NCLBUTTONDOWN:
case WM_NCLBUTTONUP:
case WM_NCLBUTTONDBLCLK:
case WM_NCRBUTTONDOWN:
case WM_NCRBUTTONUP:
case WM_NCRBUTTONDBLCLK:
case WM_NCMBUTTONDOWN:
case WM_NCMBUTTONUP:
case WM_NCMBUTTONDBLCLK:
msg.hwnd = hwndPopup;
break;
// We need to steal all keyboard messages, too.
case WM_KEYDOWN:
case WM_KEYUP:
case WM_CHAR:
case WM_DEADCHAR:
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_SYSCHAR:
case WM_SYSDEADCHAR:
msg.hwnd = hwndPopup;
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
// Something may have happened that caused us to stop.
if (cps.fDone)
{
break;
}
// If our owner stopped being the active window (e.g. the user
// Alt+Tab'd to another window in the meantime), then stop.
hwndActive = GetActiveWindow();
if (hwndActive != hwndOwner && !IsChild(hwndActive, hwndOwner) ||
GetCapture() != hwndOwner)
{
break;
}
}
// Clean up the capture we created.
ReleaseCapture();
DestroyWindow(hwndPopup);
// Clean up any theme data.
if (cps.hTheme)
{
CloseThemeData(cps.hTheme);
}
// If we got a WM_QUIT message, then re-post it so the caller's message
// loop will see it.
if (msg.message == WM_QUIT)
{
PostQuitMessage((int)msg.wParam);
}
return cps.iResult;
}
//
// FakeMenuDemo_OnEraseBkgnd
//
// Erase the background in the selected color.
//
void FakeMenuDemo_OnEraseBkgnd(HWND hwnd, HDC hdc)
{
RECT rc;
GetClientRect(hwnd, &rc);
FillRectClr(hdc, &rc, g_clrBackground);
}
//
// FakeMenuDemo_OnContextMenu
//
// Display the color-picker pseudo-menu so the user can change
// the color.
//
void FakeMenuDemo_OnContextMenu(HWND hwnd, int x, int y)
{
// If the coordinates are (-1, -1), then the user used the keyboard -
// we'll pretend the user clicked at client (0, 0).
if (x == -1 && y == -1)
{
POINT pt;
pt.x = 0;
pt.y = 0;
ClientToScreen(hwnd, &pt);
x = pt.x;
y = pt.y;
}
int iColor = ColorPick_Popup(hwnd, x, y);
// If the user picked a color, then change to that color.
if (iColor >= 0)
{
g_clrBackground = c_rgclrPredef[iColor];
InvalidateRect(hwnd, NULL, TRUE);
}
}
//
// FakeMenuDemo_WndProc
//
// Window procedure for the fake menu demo.
//
LRESULT CALLBACK FakeMenuDemo_WndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch (uiMsg)
{
case WM_ERASEBKGND:
FakeMenuDemo_OnEraseBkgnd(hwnd, (HDC)wParam);
return TRUE;
case WM_CONTEXTMENU:
FakeMenuDemo_OnContextMenu(hwnd, (short)LOWORD(lParam),
(short)HIWORD(lParam));
return 0;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, uiMsg, wParam, lParam);
}
//
// Program entry point - demonstrate pseudo-menus.
//
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE /* hInstPrev */, LPWSTR /* pszCmdLine */, int nCmdShow)
{
g_hInstance = hInstance;
WNDCLASSEX wc = { sizeof(wc) };
wc.lpfnWndProc = ColorPick_WndProc;
wc.hInstance = g_hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
wc.lpszClassName = L"ColorPick";
RegisterClassEx(&wc);
wc.style = 0;
wc.lpfnWndProc = FakeMenuDemo_WndProc;
wc.hInstance = g_hInstance;
wc.hbrBackground = NULL; // Background color is dynamic
wc.lpszClassName = L"FakeMenuDemo";
RegisterClassEx(&wc);
g_clrBackground = RGB(0xFF, 0xFF, 0xFF);
HWND hwnd = CreateWindow(
L"FakeMenuDemo",
L"Fake Menu Demo - Right-click in window to change color",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
NULL,
g_hInstance,
0);
ShowWindow(hwnd, nCmdShow);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}