598 lines
15 KiB
C++
598 lines
15 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 "MainWindow.h"
|
|
#include "TimerEventHandler.h"
|
|
#include "UIAnimationSample.h"
|
|
|
|
#include <UXTheme.h>
|
|
|
|
using namespace Gdiplus;
|
|
|
|
const DOUBLE COLOR_MIN = 0.0;
|
|
const DOUBLE COLOR_MAX = 255.0;
|
|
|
|
CMainWindow::CMainWindow() :
|
|
m_hwnd(NULL),
|
|
m_pAnimationManager(NULL),
|
|
m_pAnimationTimer(NULL),
|
|
m_pTransitionLibrary(NULL),
|
|
m_pAnimationVariableRed(NULL),
|
|
m_pAnimationVariableGreen(NULL),
|
|
m_pAnimationVariableBlue(NULL)
|
|
{
|
|
}
|
|
|
|
CMainWindow::~CMainWindow()
|
|
{
|
|
// Animated Variables
|
|
|
|
SafeRelease(&m_pAnimationVariableRed);
|
|
SafeRelease(&m_pAnimationVariableGreen);
|
|
SafeRelease(&m_pAnimationVariableBlue);
|
|
|
|
// Animation
|
|
|
|
SafeRelease(&m_pAnimationManager);
|
|
SafeRelease(&m_pAnimationTimer);
|
|
SafeRelease(&m_pTransitionLibrary);
|
|
}
|
|
|
|
// Creates the CMainWindow window
|
|
|
|
HRESULT CMainWindow::Initialize(
|
|
HINSTANCE hInstance
|
|
)
|
|
{
|
|
// Register the window class
|
|
|
|
WNDCLASSEX wcex = {sizeof(wcex)};
|
|
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
|
wcex.lpfnWndProc = CMainWindow::WndProc;
|
|
wcex.cbWndExtra = sizeof(LONG_PTR);
|
|
wcex.hInstance = hInstance;
|
|
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
|
|
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wcex.lpszClassName = L"WAMMainWindow";
|
|
|
|
RegisterClassEx(&wcex);
|
|
|
|
// Create the CMainWindow window
|
|
|
|
m_hwnd = CreateWindow(
|
|
wcex.lpszClassName,
|
|
L"Windows Animation - Timer-Driven Animation Demo",
|
|
WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
NULL,
|
|
NULL,
|
|
hInstance,
|
|
this
|
|
);
|
|
HRESULT hr = m_hwnd ? S_OK : E_FAIL;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Initialize Animation
|
|
hr = InitializeAnimation();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CreateAnimationVariables();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Display the window
|
|
ShowWindow(m_hwnd, SW_SHOWNORMAL);
|
|
UpdateWindow(m_hwnd);
|
|
|
|
// Fade in with Red
|
|
hr = ChangeColor(COLOR_MAX, COLOR_MIN, COLOR_MIN);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Invalidates the client area for redrawing
|
|
|
|
HRESULT CMainWindow::Invalidate()
|
|
{
|
|
BOOL bResult = InvalidateRect(
|
|
m_hwnd,
|
|
NULL,
|
|
FALSE
|
|
);
|
|
|
|
HRESULT hr = bResult ? S_OK : E_FAIL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Creates and initializes the main animation components
|
|
|
|
HRESULT CMainWindow::InitializeAnimation()
|
|
{
|
|
// Create Animation Manager
|
|
|
|
HRESULT hr = CoCreateInstance(
|
|
CLSID_UIAnimationManager,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_PPV_ARGS(&m_pAnimationManager)
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Create Animation Timer
|
|
|
|
hr = CoCreateInstance(
|
|
CLSID_UIAnimationTimer,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_PPV_ARGS(&m_pAnimationTimer)
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Create Animation Transition Library
|
|
|
|
hr = CoCreateInstance(
|
|
CLSID_UIAnimationTransitionLibrary,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_PPV_ARGS(&m_pTransitionLibrary)
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Connect the animation manager to the timer.
|
|
// UI_ANIMATION_IDLE_BEHAVIOR_DISABLE tells the timer to shut itself
|
|
// off when there is nothing to animate.
|
|
|
|
IUIAnimationTimerUpdateHandler *pTimerUpdateHandler;
|
|
hr = m_pAnimationManager->QueryInterface(
|
|
IID_PPV_ARGS(&pTimerUpdateHandler)
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pAnimationTimer->SetTimerUpdateHandler(
|
|
pTimerUpdateHandler,
|
|
UI_ANIMATION_IDLE_BEHAVIOR_DISABLE
|
|
);
|
|
pTimerUpdateHandler->Release();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Create and set the Timer Event Handler
|
|
|
|
IUIAnimationTimerEventHandler *pTimerEventHandler;
|
|
hr = CTimerEventHandler::CreateInstance(
|
|
this,
|
|
&pTimerEventHandler
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pAnimationTimer->SetTimerEventHandler(
|
|
pTimerEventHandler
|
|
);
|
|
pTimerEventHandler->Release();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Creates the RGB animation variables for the background color
|
|
|
|
HRESULT CMainWindow::CreateAnimationVariables()
|
|
{
|
|
const DOUBLE INITIAL_RED = COLOR_MAX;
|
|
const DOUBLE INITIAL_GREEN = COLOR_MAX;
|
|
const DOUBLE INITIAL_BLUE = COLOR_MAX;
|
|
|
|
HRESULT hr = m_pAnimationManager->CreateAnimationVariable(
|
|
INITIAL_RED,
|
|
&m_pAnimationVariableRed
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pAnimationVariableRed->SetLowerBound(COLOR_MIN);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pAnimationVariableRed->SetUpperBound(COLOR_MAX);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pAnimationManager->CreateAnimationVariable(
|
|
INITIAL_GREEN,
|
|
&m_pAnimationVariableGreen
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pAnimationVariableGreen->SetLowerBound(COLOR_MIN);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pAnimationVariableGreen->SetUpperBound(COLOR_MAX);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pAnimationManager->CreateAnimationVariable(
|
|
INITIAL_BLUE,
|
|
&m_pAnimationVariableBlue
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pAnimationVariableBlue->SetLowerBound(COLOR_MIN);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pAnimationVariableBlue->SetUpperBound(COLOR_MAX);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// The window message handler
|
|
|
|
LRESULT CALLBACK CMainWindow::WndProc(
|
|
HWND hwnd,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
const int MESSAGE_PROCESSED = 0;
|
|
|
|
if (uMsg == WM_CREATE)
|
|
{
|
|
LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
|
|
CMainWindow *pMainWindow = (CMainWindow *)pcs->lpCreateParams;
|
|
|
|
SetWindowLongPtrW(
|
|
hwnd,
|
|
GWLP_USERDATA,
|
|
PtrToUlong(pMainWindow)
|
|
);
|
|
|
|
return MESSAGE_PROCESSED;
|
|
}
|
|
|
|
CMainWindow *pMainWindow = reinterpret_cast<CMainWindow *>(static_cast<LONG_PTR>(
|
|
GetWindowLongPtrW(
|
|
hwnd,
|
|
GWLP_USERDATA
|
|
)));
|
|
|
|
if (pMainWindow != NULL)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_LBUTTONDOWN:
|
|
{
|
|
pMainWindow->OnLButtonDown();
|
|
}
|
|
return MESSAGE_PROCESSED;
|
|
|
|
case WM_PAINT:
|
|
case WM_DISPLAYCHANGE:
|
|
{
|
|
PAINTSTRUCT ps;
|
|
BeginPaint(
|
|
hwnd,
|
|
&ps
|
|
);
|
|
|
|
pMainWindow->OnPaint(
|
|
ps.hdc,
|
|
ps.rcPaint
|
|
);
|
|
|
|
EndPaint(
|
|
hwnd,
|
|
&ps
|
|
);
|
|
}
|
|
return MESSAGE_PROCESSED;
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
pMainWindow->OnDestroy();
|
|
|
|
PostQuitMessage(
|
|
0
|
|
);
|
|
}
|
|
return MESSAGE_PROCESSED;
|
|
}
|
|
}
|
|
return DefWindowProc(
|
|
hwnd,
|
|
uMsg,
|
|
wParam,
|
|
lParam
|
|
);
|
|
}
|
|
|
|
// Called whenever the client area needs to be drawn
|
|
|
|
HRESULT CMainWindow::OnPaint(
|
|
HDC hdc,
|
|
const RECT &rcPaint
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
HDC hdcBuffer;
|
|
HPAINTBUFFER hpb = BeginBufferedPaint(
|
|
hdc,
|
|
&rcPaint,
|
|
BPBF_COMPATIBLEBITMAP,
|
|
NULL,
|
|
&hdcBuffer
|
|
);
|
|
if (hpb)
|
|
{
|
|
hr = DrawClientArea(
|
|
hdcBuffer,
|
|
rcPaint
|
|
);
|
|
|
|
// Can only return one failure HRESULT
|
|
HRESULT hrEndDraw = EndBufferedPaint(
|
|
hpb,
|
|
TRUE
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = hrEndDraw;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// An error occurred, default to unbuffered painting
|
|
|
|
hr = DrawClientArea(
|
|
hdc,
|
|
rcPaint
|
|
);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// When the left mouse button is clicked on the client area, schedule
|
|
// animations to change the background color of the window
|
|
|
|
HRESULT CMainWindow::OnLButtonDown()
|
|
{
|
|
HRESULT hr = ChangeColor(
|
|
RandomFromRange(COLOR_MIN, COLOR_MAX),
|
|
RandomFromRange(COLOR_MIN, COLOR_MAX),
|
|
RandomFromRange(COLOR_MIN, COLOR_MAX)
|
|
);
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Prepares for window destruction
|
|
|
|
void CMainWindow::OnDestroy()
|
|
{
|
|
if (m_pAnimationTimer != NULL)
|
|
{
|
|
// Clear the timer event handler, to ensure that the one that points to this object is released
|
|
|
|
(void)m_pAnimationTimer->SetTimerEventHandler(NULL);
|
|
}
|
|
}
|
|
|
|
// Draws the contents of the client area
|
|
|
|
HRESULT CMainWindow::DrawClientArea(
|
|
HDC hdc,
|
|
const RECT &rcPaint
|
|
)
|
|
{
|
|
Graphics graphics(hdc);
|
|
HRESULT hr = HrFromStatus(graphics.SetSmoothingMode(
|
|
SmoothingModeAntiAlias
|
|
));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
RectF rectPaint(
|
|
static_cast<REAL>(rcPaint.left),
|
|
static_cast<REAL>(rcPaint.top),
|
|
static_cast<REAL>(rcPaint.right - rcPaint.left),
|
|
static_cast<REAL>(rcPaint.bottom - rcPaint.top)
|
|
);
|
|
|
|
hr = DrawBackground(
|
|
graphics,
|
|
rectPaint
|
|
);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Fills the background with the color specified by the animation variables
|
|
|
|
HRESULT CMainWindow::DrawBackground(
|
|
Graphics &graphics,
|
|
const RectF &rectPaint
|
|
)
|
|
{
|
|
// Get the RGB animation variable values
|
|
|
|
INT32 red;
|
|
HRESULT hr = m_pAnimationVariableRed->GetIntegerValue(
|
|
&red
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
INT32 green;
|
|
hr = m_pAnimationVariableGreen->GetIntegerValue(
|
|
&green
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
INT32 blue;
|
|
hr = m_pAnimationVariableBlue->GetIntegerValue(
|
|
&blue
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Set the RGB of the background brush to the new animated value
|
|
|
|
SolidBrush brushBackground(Color(
|
|
static_cast<BYTE>(red),
|
|
static_cast<BYTE>(green),
|
|
static_cast<BYTE>(blue)
|
|
));
|
|
|
|
// Paint the background
|
|
|
|
hr = HrFromStatus(graphics.FillRectangle(
|
|
&brushBackground,
|
|
rectPaint
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Animates the background color to a new value
|
|
|
|
HRESULT CMainWindow::ChangeColor(
|
|
DOUBLE red,
|
|
DOUBLE green,
|
|
DOUBLE blue
|
|
)
|
|
{
|
|
const UI_ANIMATION_SECONDS DURATION = 0.5;
|
|
const DOUBLE ACCELERATION_RATIO = 0.5;
|
|
const DOUBLE DECELERATION_RATIO = 0.5;
|
|
|
|
// Create a storyboard
|
|
|
|
IUIAnimationStoryboard *pStoryboard = NULL;
|
|
HRESULT hr = m_pAnimationManager->CreateStoryboard(
|
|
&pStoryboard
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Create transitions for the RGB animation variables
|
|
|
|
IUIAnimationTransition *pTransitionRed;
|
|
hr = m_pTransitionLibrary->CreateAccelerateDecelerateTransition(
|
|
DURATION,
|
|
red,
|
|
ACCELERATION_RATIO,
|
|
DECELERATION_RATIO,
|
|
&pTransitionRed
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IUIAnimationTransition *pTransitionGreen;
|
|
hr = m_pTransitionLibrary->CreateAccelerateDecelerateTransition(
|
|
DURATION,
|
|
green,
|
|
ACCELERATION_RATIO,
|
|
DECELERATION_RATIO,
|
|
&pTransitionGreen
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IUIAnimationTransition *pTransitionBlue;
|
|
hr = m_pTransitionLibrary->CreateAccelerateDecelerateTransition(
|
|
DURATION,
|
|
blue,
|
|
ACCELERATION_RATIO,
|
|
DECELERATION_RATIO,
|
|
&pTransitionBlue
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Add transitions to the storyboard
|
|
|
|
hr = pStoryboard->AddTransition(
|
|
m_pAnimationVariableRed,
|
|
pTransitionRed
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pStoryboard->AddTransition(
|
|
m_pAnimationVariableGreen,
|
|
pTransitionGreen
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pStoryboard->AddTransition(
|
|
m_pAnimationVariableBlue,
|
|
pTransitionBlue
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Get the current time and schedule the storyboard for play
|
|
|
|
UI_ANIMATION_SECONDS secondsNow;
|
|
hr = m_pAnimationTimer->GetTime(
|
|
&secondsNow
|
|
);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pStoryboard->Schedule(
|
|
secondsNow
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pTransitionBlue->Release();
|
|
}
|
|
|
|
pTransitionGreen->Release();
|
|
}
|
|
|
|
pTransitionRed->Release();
|
|
}
|
|
|
|
pStoryboard->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Generate a random value in the given range
|
|
|
|
DOUBLE CMainWindow::RandomFromRange(
|
|
DOUBLE minimum,
|
|
DOUBLE maximum
|
|
)
|
|
{
|
|
return minimum + (maximum - minimum) * rand() / RAND_MAX;
|
|
}
|
|
|
|
// Convert a GDI+ Status to an HRESULT
|
|
|
|
HRESULT CMainWindow::HrFromStatus(
|
|
Status status
|
|
)
|
|
{
|
|
return (status == Ok ? S_OK : E_FAIL);
|
|
}
|