//-------------------------------------------------------------------------------------- // File: ControllerFont.h // // Class for compositing text with Xbox controller font button sprites // // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. //------------------------------------------------------------------------------------- #pragma once #include "SpriteBatch.h" #include "SpriteFont.h" namespace DX { enum class ControllerFont : wchar_t { LeftThumb = L' ', DPad = L'!', RightThumb = L'\"', View = L'#', // Xbox One name Back = L'#', // Xbox 360 name Nexus = L'$', // Xbox One name Guide = L'$', // Xbox 360 name Menu = L'%', // Xbox One name Start = L'%', // Xbox 360 name XButton = L'&', AButton = L'\'', YButton = L'(', BButton = L')', RightShoulder = L'*', RightTrigger = L'+', LeftTrigger = L',', LeftShoulder = L'-', }; inline void XM_CALLCONV DrawControllerString( _In_ DirectX::SpriteBatch* spriteBatch, _In_ const DirectX::SpriteFont* textFont, _In_ const DirectX::SpriteFont* butnFont, _In_z_ wchar_t const* text, DirectX::XMFLOAT2 const& position, DirectX::FXMVECTOR color = DirectX::Colors::White, float scale = 1) { using namespace DirectX; const size_t textLen = wcslen(text); if (textLen >= 4096) { throw std::out_of_range("String is too long"); } const float buttonHeight = butnFont->GetLineSpacing(); const float buttonScale = (textFont->GetLineSpacing() * scale) / buttonHeight; const float offsetY = buttonScale / 2.f; size_t j = 0; wchar_t strBuffer[4096] = {}; bool buttonText = false; XMFLOAT2 outPos = position; for (size_t ch = 0; ch < textLen; ++ch) { if (buttonText) { strBuffer[j++] = text[ch]; if (text[ch] == L']') { wchar_t button[2] = {}; if (_wcsicmp(strBuffer, L"[A]") == 0) { *button = static_cast(ControllerFont::AButton); } else if (_wcsicmp(strBuffer, L"[B]") == 0) { *button = static_cast(ControllerFont::BButton); } else if (_wcsicmp(strBuffer, L"[X]") == 0) { *button = static_cast(ControllerFont::XButton); } else if (_wcsicmp(strBuffer, L"[Y]") == 0) { *button = static_cast(ControllerFont::YButton); } else if (_wcsicmp(strBuffer, L"[DPad]") == 0) { *button = static_cast(ControllerFont::DPad); } else if (_wcsicmp(strBuffer, L"[View]") == 0 || _wcsicmp(strBuffer, L"[Back]") == 0) { *button = static_cast(ControllerFont::View); } else if (_wcsicmp(strBuffer, L"[Menu]") == 0 || _wcsicmp(strBuffer, L"[Start]") == 0) { *button = static_cast(ControllerFont::Menu); } else if (_wcsicmp(strBuffer, L"[Nexus]") == 0 || _wcsicmp(strBuffer, L"[Guide]") == 0) { *button = static_cast(ControllerFont::Nexus); } else if (_wcsicmp(strBuffer, L"[RThumb]") == 0) { *button = static_cast(ControllerFont::RightThumb); } else if (_wcsicmp(strBuffer, L"[LThumb]") == 0) { *button = static_cast(ControllerFont::LeftThumb); } else if (_wcsicmp(strBuffer, L"[RB]") == 0) { *button = static_cast(ControllerFont::RightShoulder); } else if (_wcsicmp(strBuffer, L"[LB]") == 0) { *button = static_cast(ControllerFont::LeftShoulder); } else if (_wcsicmp(strBuffer, L"[RT]") == 0) { *button = static_cast(ControllerFont::RightTrigger); } else if (_wcsicmp(strBuffer, L"[LT]") == 0) { *button = static_cast(ControllerFont::LeftTrigger); } if (*button) { const float bsize = XMVectorGetX(butnFont->MeasureString(button)); const float offsetX = (bsize * buttonScale / 2.f); outPos.x += offsetX; outPos.y -= offsetY; butnFont->DrawString(spriteBatch, button, outPos, Colors::White, 0.f, XMFLOAT2(0.f, 0.f), XMFLOAT2(buttonScale, buttonScale)); outPos.x += bsize * buttonScale; outPos.y += offsetY; } memset(strBuffer, 0, sizeof(strBuffer)); j = 0; buttonText = false; } } else { switch (text[ch]) { case '\r': break; case '[': if (*strBuffer) { textFont->DrawString(spriteBatch, strBuffer, outPos, color, 0.f, XMFLOAT2(0.f, 0.f), XMFLOAT2(scale, scale)); outPos.x += XMVectorGetX(textFont->MeasureString(strBuffer)) * scale; memset(strBuffer, 0, sizeof(strBuffer)); j = 0; } buttonText = true; *strBuffer = L'['; ++j; break; case '\n': if (*strBuffer) { textFont->DrawString(spriteBatch, strBuffer, outPos, color, 0.f, XMFLOAT2(0.f, 0.f), XMFLOAT2(scale, scale)); memset(strBuffer, 0, sizeof(strBuffer)); j = 0; } outPos.x = position.x; outPos.y += textFont->GetLineSpacing() * scale; break; default: strBuffer[j++] = text[ch]; break; } } } if (*strBuffer) { textFont->DrawString(spriteBatch, strBuffer, outPos, color, 0.f, XMFLOAT2(0.f, 0.f), XMFLOAT2(scale, scale)); } } inline RECT XM_CALLCONV MeasureControllerDrawBounds( _In_ const DirectX::SpriteFont* textFont, _In_ const DirectX::SpriteFont* butnFont, _In_z_ wchar_t const* text, DirectX::XMFLOAT2 const& position, float scale = 1) { using namespace DirectX; const size_t textLen = wcslen(text); if (textLen >= 4096) { throw std::out_of_range("String is too long"); } const float buttonHeight = butnFont->GetLineSpacing(); const float buttonScale = (textFont->GetLineSpacing() * scale) / buttonHeight; const float offsetY = buttonScale / 2.f; size_t j = 0; wchar_t strBuffer[4096] = {}; bool buttonText = false; XMFLOAT2 outPos = position; RECT result = { LONG_MAX, LONG_MAX, 0, 0 }; for (size_t ch = 0; ch < textLen; ++ch) { if (buttonText) { strBuffer[j++] = text[ch]; if (text[ch] == L']') { wchar_t button[2] = {}; if (_wcsicmp(strBuffer, L"[A]") == 0) { *button = static_cast(ControllerFont::AButton); } else if (_wcsicmp(strBuffer, L"[B]") == 0) { *button = static_cast(ControllerFont::BButton); } else if (_wcsicmp(strBuffer, L"[X]") == 0) { *button = static_cast(ControllerFont::XButton); } else if (_wcsicmp(strBuffer, L"[Y]") == 0) { *button = static_cast(ControllerFont::YButton); } else if (_wcsicmp(strBuffer, L"[DPad]") == 0) { *button = static_cast(ControllerFont::DPad); } else if (_wcsicmp(strBuffer, L"[View]") == 0 || _wcsicmp(strBuffer, L"[Back]") == 0) { *button = static_cast(ControllerFont::View); } else if (_wcsicmp(strBuffer, L"[Menu]") == 0 || _wcsicmp(strBuffer, L"[Start]") == 0) { *button = static_cast(ControllerFont::Menu); } else if (_wcsicmp(strBuffer, L"[Nexus]") == 0 || _wcsicmp(strBuffer, L"[Guide]") == 0) { *button = static_cast(ControllerFont::Nexus); } else if (_wcsicmp(strBuffer, L"[RThumb]") == 0) { *button = static_cast(ControllerFont::RightThumb); } else if (_wcsicmp(strBuffer, L"[LThumb]") == 0) { *button = static_cast(ControllerFont::LeftThumb); } else if (_wcsicmp(strBuffer, L"[RB]") == 0) { *button = static_cast(ControllerFont::RightShoulder); } else if (_wcsicmp(strBuffer, L"[LB]") == 0) { *button = static_cast(ControllerFont::LeftShoulder); } else if (_wcsicmp(strBuffer, L"[RT]") == 0) { *button = static_cast(ControllerFont::RightTrigger); } else if (_wcsicmp(strBuffer, L"[LT]") == 0) { *button = static_cast(ControllerFont::LeftTrigger); } if (*button) { const float bsize = XMVectorGetX(butnFont->MeasureString(button)); const float offsetX = (bsize * buttonScale / 2.f); if (outPos.x < float(result.left)) result.left = long(outPos.x); if (outPos.y < float(result.top)) result.top = long(outPos.y); outPos.x += offsetX; outPos.y -= offsetY; if (outPos.x < float(result.left)) result.left = long(outPos.x); if (outPos.y < float(result.top)) result.top = long(outPos.y); outPos.x += bsize * buttonScale; outPos.y += offsetY; if (float(result.right) < outPos.x) result.right = long(outPos.x); if (float(result.bottom) < outPos.y) result.bottom = long(outPos.y); } memset(strBuffer, 0, sizeof(strBuffer)); j = 0; buttonText = false; } } else { switch (text[ch]) { case '\r': break; case '[': if (*strBuffer) { if (outPos.x < float(result.left)) result.left = long(outPos.x); if (outPos.y < float(result.top)) result.top = long(outPos.y); outPos.x += XMVectorGetX(textFont->MeasureString(strBuffer)) * scale; if (float(result.right) < outPos.x) result.right = long(outPos.x); if (float(result.bottom) < outPos.y) result.bottom = long(outPos.y); memset(strBuffer, 0, sizeof(strBuffer)); j = 0; } buttonText = true; *strBuffer = L'['; ++j; break; case '\n': if (*strBuffer) { if (outPos.x < float(result.left)) result.left = long(outPos.x); if (outPos.y < float(result.top)) result.top = long(outPos.y); outPos.x += XMVectorGetX(textFont->MeasureString(strBuffer)) * scale; if (float(result.right) < outPos.x) result.right = long(outPos.x); if (float(result.bottom) < outPos.y) result.bottom = long(outPos.y); memset(strBuffer, 0, sizeof(strBuffer)); j = 0; } outPos.x = position.x; outPos.y += textFont->GetLineSpacing() * scale; break; default: strBuffer[j++] = text[ch]; break; } } } if (*strBuffer) { if (outPos.x < float(result.left)) result.left = long(outPos.x); if (outPos.y < float(result.top)) result.top = long(outPos.y); outPos.x += XMVectorGetX(textFont->MeasureString(strBuffer)) * scale; if (float(result.right) < outPos.x) result.right = long(outPos.x); if (float(result.bottom) < outPos.y) result.bottom = long(outPos.y); } if (result.left == LONG_MAX) { result.left = 0; result.top = 0; } return result; } }