Saxum/extern/bullet/Demos/DX11ClothDemo/DXUT/Optional/DXUTguiIME.cpp

991 lines
36 KiB
C++
Raw Normal View History

2014-10-24 09:42:47 +00:00
//--------------------------------------------------------------------------------------
// File: DXUTguiIME.cpp
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//--------------------------------------------------------------------------------------
#include "DXUT.h"
#include "DXUTgui.h"
#include "DXUTsettingsDlg.h"
#include "DXUTres.h"
#include "DXUTgui.h"
#include "DXUTguiIME.h"
#undef min // use __min instead
#undef max // use __max instead
#define DXUT_NEAR_BUTTON_DEPTH 0.6f
//--------------------------------------------------------------------------------------
// CDXUTIMEEditBox class
//--------------------------------------------------------------------------------------
// IME constants
POINT CDXUTIMEEditBox::s_ptCompString; // Composition string position. Updated every frame.
int CDXUTIMEEditBox::s_nFirstTargetConv; // Index of the first target converted char in comp string. If none, -1.
CUniBuffer CDXUTIMEEditBox::s_CompString = CUniBuffer( 0 );
DWORD CDXUTIMEEditBox::s_adwCompStringClause[MAX_COMPSTRING_SIZE];
WCHAR CDXUTIMEEditBox::s_wszReadingString[32];
CDXUTIMEEditBox::CCandList CDXUTIMEEditBox::s_CandList; // Data relevant to the candidate list
bool CDXUTIMEEditBox::s_bImeFlag = true;
#if defined(DEBUG) || defined(_DEBUG)
bool CDXUTIMEEditBox::m_bIMEStaticMsgProcCalled = false;
#endif
//--------------------------------------------------------------------------------------
HRESULT CDXUTIMEEditBox::CreateIMEEditBox( CDXUTDialog* pDialog, int ID, LPCWSTR strText, int x, int y, int width,
int height, bool bIsDefault, CDXUTIMEEditBox** ppCreated )
{
CDXUTIMEEditBox* pEditBox = new CDXUTIMEEditBox( pDialog );
if( ppCreated != NULL )
*ppCreated = pEditBox;
if( pEditBox == NULL )
return E_OUTOFMEMORY;
// Set the ID and position
pEditBox->SetID( ID );
pEditBox->SetLocation( x, y );
pEditBox->SetSize( width, height );
pEditBox->m_bIsDefault = bIsDefault;
if( strText )
pEditBox->SetText( strText );
return S_OK;
}
//--------------------------------------------------------------------------------------
void CDXUTIMEEditBox::InitDefaultElements( CDXUTDialog* pDialog )
{
//-------------------------------------
// CDXUTIMEEditBox
//-------------------------------------
CDXUTElement Element;
RECT rcTexture;
Element.SetFont( 0, D3DCOLOR_ARGB( 255, 0, 0, 0 ), DT_LEFT | DT_TOP );
// Assign the style
SetRect( &rcTexture, 14, 90, 241, 113 );
Element.SetTexture( 0, &rcTexture );
pDialog->SetDefaultElement( DXUT_CONTROL_IMEEDITBOX, 0, &Element );
SetRect( &rcTexture, 8, 82, 14, 90 );
Element.SetTexture( 0, &rcTexture );
pDialog->SetDefaultElement( DXUT_CONTROL_IMEEDITBOX, 1, &Element );
SetRect( &rcTexture, 14, 82, 241, 90 );
Element.SetTexture( 0, &rcTexture );
pDialog->SetDefaultElement( DXUT_CONTROL_IMEEDITBOX, 2, &Element );
SetRect( &rcTexture, 241, 82, 246, 90 );
Element.SetTexture( 0, &rcTexture );
pDialog->SetDefaultElement( DXUT_CONTROL_IMEEDITBOX, 3, &Element );
SetRect( &rcTexture, 8, 90, 14, 113 );
Element.SetTexture( 0, &rcTexture );
pDialog->SetDefaultElement( DXUT_CONTROL_IMEEDITBOX, 4, &Element );
SetRect( &rcTexture, 241, 90, 246, 113 );
Element.SetTexture( 0, &rcTexture );
pDialog->SetDefaultElement( DXUT_CONTROL_IMEEDITBOX, 5, &Element );
SetRect( &rcTexture, 8, 113, 14, 121 );
Element.SetTexture( 0, &rcTexture );
pDialog->SetDefaultElement( DXUT_CONTROL_IMEEDITBOX, 6, &Element );
SetRect( &rcTexture, 14, 113, 241, 121 );
Element.SetTexture( 0, &rcTexture );
pDialog->SetDefaultElement( DXUT_CONTROL_IMEEDITBOX, 7, &Element );
SetRect( &rcTexture, 241, 113, 246, 121 );
Element.SetTexture( 0, &rcTexture );
pDialog->SetDefaultElement( DXUT_CONTROL_IMEEDITBOX, 8, &Element );
// Element 9 for IME text, and indicator button
SetRect( &rcTexture, 0, 0, 136, 54 );
Element.SetTexture( 0, &rcTexture );
Element.SetFont( 0, D3DCOLOR_ARGB( 255, 0, 0, 0 ), DT_CENTER | DT_VCENTER );
pDialog->SetDefaultElement( DXUT_CONTROL_IMEEDITBOX, 9, &Element );
}
//--------------------------------------------------------------------------------------
CDXUTIMEEditBox::CDXUTIMEEditBox( CDXUTDialog* pDialog )
{
m_Type = DXUT_CONTROL_IMEEDITBOX;
m_pDialog = pDialog;
m_nIndicatorWidth = 0;
m_ReadingColor = D3DCOLOR_ARGB( 188, 255, 255, 255 );
m_ReadingWinColor = D3DCOLOR_ARGB( 128, 0, 0, 0 );
m_ReadingSelColor = D3DCOLOR_ARGB( 255, 255, 0, 0 );
m_ReadingSelBkColor = D3DCOLOR_ARGB( 128, 80, 80, 80 );
m_CandidateColor = D3DCOLOR_ARGB( 255, 200, 200, 200 );
m_CandidateWinColor = D3DCOLOR_ARGB( 128, 0, 0, 0 );
m_CandidateSelColor = D3DCOLOR_ARGB( 255, 255, 255, 255 );
m_CandidateSelBkColor = D3DCOLOR_ARGB( 128, 158, 158, 158 );
m_CompColor = D3DCOLOR_ARGB( 255, 200, 200, 255 );
m_CompWinColor = D3DCOLOR_ARGB( 198, 0, 0, 0 );
m_CompCaretColor = D3DCOLOR_ARGB( 255, 255, 255, 255 );
m_CompTargetColor = D3DCOLOR_ARGB( 255, 255, 255, 255 );
m_CompTargetBkColor = D3DCOLOR_ARGB( 255, 150, 150, 150 );
m_CompTargetNonColor = D3DCOLOR_ARGB( 255, 255, 255, 0 );
m_CompTargetNonBkColor = D3DCOLOR_ARGB( 255, 150, 150, 150 );
m_IndicatorImeColor = D3DCOLOR_ARGB( 255, 255, 255, 255 );
m_IndicatorEngColor = D3DCOLOR_ARGB( 255, 0, 0, 0 );
m_IndicatorBkColor = D3DCOLOR_ARGB( 255, 128, 128, 128 );
}
//--------------------------------------------------------------------------------------
CDXUTIMEEditBox::~CDXUTIMEEditBox()
{
}
//--------------------------------------------------------------------------------------
void CDXUTIMEEditBox::SendKey( BYTE nVirtKey )
{
keybd_event( nVirtKey, 0, 0, 0 );
keybd_event( nVirtKey, 0, KEYEVENTF_KEYUP, 0 );
}
//--------------------------------------------------------------------------------------
void CDXUTIMEEditBox::UpdateRects()
{
// Temporary adjust m_width so that CDXUTEditBox can compute
// the correct rects for its rendering since we need to make space
// for the indicator button
int nWidth = m_width;
m_width -= m_nIndicatorWidth + m_nBorder * 2; // Make room for the indicator button
CDXUTEditBox::UpdateRects();
m_width = nWidth; // Restore
// Compute the indicator button rectangle
SetRect( &m_rcIndicator, m_rcBoundingBox.right, m_rcBoundingBox.top, m_x + m_width, m_rcBoundingBox.bottom );
// InflateRect( &m_rcIndicator, -m_nBorder, -m_nBorder );
m_rcBoundingBox.right = m_rcBoundingBox.left + m_width;
}
//--------------------------------------------------------------------------------------
// GetImeId( UINT uIndex )
// returns
// returned value:
// 0: In the following cases
// - Non Chinese IME input locale
// - Older Chinese IME
// - Other error cases
//
// Othewise:
// When uIndex is 0 (default)
// bit 31-24: Major version
// bit 23-16: Minor version
// bit 15-0: Language ID
// When uIndex is 1
// pVerFixedInfo->dwFileVersionLS
//
// Use IMEID_VER and IMEID_LANG macro to extract version and language information.
//
// We define the locale-invariant ID ourselves since it doesn't exist prior to WinXP
// For more information, see the CompareString() reference.
#define LCID_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
//--------------------------------------------------------------------------------------
// Enable/disable the entire IME system. When disabled, the default IME handling
// kicks in.
void CDXUTIMEEditBox::EnableImeSystem( bool bEnable )
{
ImeUi_EnableIme( bEnable );
}
//--------------------------------------------------------------------------------------
// Resets the composition string.
void CDXUTIMEEditBox::ResetCompositionString()
{
s_CompString.SetText( L"" );
}
//--------------------------------------------------------------------------------------
// This function is used only briefly in CHT IME handling,
// so accelerator isn't processed.
void CDXUTIMEEditBox::PumpMessage()
{
MSG msg;
while( PeekMessageW( &msg, NULL, 0, 0, PM_NOREMOVE ) )
{
if( !GetMessageW( &msg, NULL, 0, 0 ) )
{
PostQuitMessage( ( int )msg.wParam );
return;
}
TranslateMessage( &msg );
DispatchMessageA( &msg );
}
}
//--------------------------------------------------------------------------------------
void CDXUTIMEEditBox::OnFocusIn()
{
ImeUi_EnableIme( s_bImeFlag );
CDXUTEditBox::OnFocusIn();
}
//--------------------------------------------------------------------------------------
void CDXUTIMEEditBox::OnFocusOut()
{
ImeUi_FinalizeString();
ImeUi_EnableIme( false );
CDXUTEditBox::OnFocusOut();
}
//--------------------------------------------------------------------------------------
bool CDXUTIMEEditBox::StaticMsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
if( !ImeUi_IsEnabled() )
return false;
#if defined(DEBUG) || defined(_DEBUG)
m_bIMEStaticMsgProcCalled = true;
#endif
switch( uMsg )
{
case WM_INPUTLANGCHANGE:
DXUTTRACE( L"WM_INPUTLANGCHANGE\n" );
{
}
return true;
case WM_IME_SETCONTEXT:
DXUTTRACE( L"WM_IME_SETCONTEXT\n" );
//
// We don't want anything to display, so we have to clear this
//
lParam = 0;
return false;
// Handle WM_IME_STARTCOMPOSITION here since
// we do not want the default IME handler to see
// this when our fullscreen app is running.
case WM_IME_STARTCOMPOSITION:
DXUTTRACE( L"WM_IME_STARTCOMPOSITION\n" );
ResetCompositionString();
// Since the composition string has its own caret, we don't render
// the edit control's own caret to avoid double carets on screen.
s_bHideCaret = true;
return true;
case WM_IME_ENDCOMPOSITION:
DXUTTRACE( L"WM_IME_ENDCOMPOSITION\n" );
s_bHideCaret = false;
return false;
case WM_IME_COMPOSITION:
DXUTTRACE( L"WM_IME_COMPOSITION\n" );
return false;
}
return false;
}
//--------------------------------------------------------------------------------------
bool CDXUTIMEEditBox::HandleMouse( UINT uMsg, POINT pt, WPARAM wParam, LPARAM lParam )
{
if( !m_bEnabled || !m_bVisible )
return false;
switch( uMsg )
{
case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK:
{
DXUTFontNode* pFont = m_pDialog->GetFont( m_Elements.GetAt( 9 )->iFont );
// Check if this click is on top of the composition string
int nCompStrWidth;
s_CompString.CPtoX( s_CompString.GetTextSize(), FALSE, &nCompStrWidth );
if( s_ptCompString.x <= pt.x &&
s_ptCompString.y <= pt.y &&
s_ptCompString.x + nCompStrWidth > pt.x &&
s_ptCompString.y + pFont->nHeight > pt.y )
{
int nCharBodyHit, nCharHit;
int nTrail;
// Determine the character clicked on.
s_CompString.XtoCP( pt.x - s_ptCompString.x, &nCharBodyHit, &nTrail );
if( nTrail && nCharBodyHit < s_CompString.GetTextSize() )
nCharHit = nCharBodyHit + 1;
else
nCharHit = nCharBodyHit;
switch( GetPrimaryLanguage() )
{
case LANG_JAPANESE:
// For Japanese, there are two cases. If s_nFirstTargetConv is
// -1, the comp string hasn't been converted yet, and we use
// s_nCompCaret. For any other value of s_nFirstTargetConv,
// the string has been converted, so we use clause information.
if( s_nFirstTargetConv != -1 )
{
int nClauseClicked = 0;
while( ( int )s_adwCompStringClause[nClauseClicked + 1] <= nCharBodyHit )
++nClauseClicked;
int nClauseSelected = 0;
while( ( int )s_adwCompStringClause[nClauseSelected + 1] <= s_nFirstTargetConv )
++nClauseSelected;
BYTE nVirtKey = nClauseClicked > nClauseSelected ? VK_RIGHT : VK_LEFT;
int nSendCount = abs( nClauseClicked - nClauseSelected );
while( nSendCount-- > 0 )
SendKey( nVirtKey );
return true;
}
// Not converted case. Fall thru to Chinese case.
case LANG_CHINESE:
{
// For Chinese, use s_nCompCaret.
BYTE nVirtKey = nCharHit > ( int )ImeUi_GetImeCursorChars() ? VK_RIGHT : VK_LEFT;
int nSendCount = abs( nCharHit - ( int )ImeUi_GetImeCursorChars() );
while( nSendCount-- > 0 )
SendKey( nVirtKey );
break;
}
}
return true;
}
// Check if the click is on top of the candidate window
if( ImeUi_IsShowCandListWindow() && PtInRect( &s_CandList.rcCandidate, pt ) )
{
if( ImeUi_IsVerticalCand() )
{
// Vertical candidate window
// Compute the row the click is on
int nRow = ( pt.y - s_CandList.rcCandidate.top ) / pFont->nHeight;
if( nRow < ( int )ImeUi_GetCandidateCount() )
{
// nRow is a valid entry.
// Now emulate keystrokes to select the candidate at this row.
switch( GetPrimaryLanguage() )
{
case LANG_CHINESE:
case LANG_KOREAN:
// For Chinese and Korean, simply send the number keystroke.
SendKey( ( BYTE )( '0' + nRow + 1 ) );
break;
case LANG_JAPANESE:
// For Japanese, move the selection to the target row,
// then send Right, then send Left.
BYTE nVirtKey;
if( nRow > ( int )ImeUi_GetCandidateSelection() )
nVirtKey = VK_DOWN;
else
nVirtKey = VK_UP;
int nNumToHit = abs( int( nRow - ImeUi_GetCandidateSelection() ) );
for( int nStrike = 0; nStrike < nNumToHit; ++nStrike )
SendKey( nVirtKey );
// Do this to close the candidate window without ending composition.
SendKey( VK_RIGHT );
SendKey( VK_LEFT );
break;
}
}
}
else
{
// Horizontal candidate window
// Determine which the character the click has hit.
int nCharHit;
int nTrail;
s_CandList.HoriCand.XtoCP( pt.x - s_CandList.rcCandidate.left, &nCharHit, &nTrail );
// Determine which candidate string the character belongs to.
int nCandidate = ImeUi_GetCandidateCount() - 1;
int nEntryStart = 0;
for( UINT i = 0; i < ImeUi_GetCandidateCount(); ++i )
{
if( nCharHit >= nEntryStart )
{
// Haven't found it.
nEntryStart += lstrlenW( ImeUi_GetCandidate( i ) ) + 1; // plus space separator
}
else
{
// Found it. This entry starts at the right side of the click point,
// so the char belongs to the previous entry.
nCandidate = i - 1;
break;
}
}
// Now emulate keystrokes to select the candidate entry.
switch( GetPrimaryLanguage() )
{
case LANG_CHINESE:
case LANG_KOREAN:
// For Chinese and Korean, simply send the number keystroke.
SendKey( ( BYTE )( '0' + nCandidate + 1 ) );
break;
}
}
return true;
}
}
}
// If we didn't care for the msg, let the parent process it.
return CDXUTEditBox::HandleMouse( uMsg, pt, wParam, lParam );
}
//--------------------------------------------------------------------------------------
bool CDXUTIMEEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam )
{
if( !m_bEnabled || !m_bVisible )
return false;
#if defined(DEBUG) || defined(_DEBUG)
// DXUT.cpp used to call CDXUTIMEEditBox::StaticMsgProc() so that, but now
// this is the application's responsiblity. To do this, call
// CDXUTDialogResourceManager::MsgProc() before calling this function.
assert( m_bIMEStaticMsgProcCalled && L"To fix, call CDXUTDialogResourceManager::MsgProc() first" );
#endif
switch( uMsg )
{
case WM_DESTROY:
ImeUi_Uninitialize();
break;
}
bool trappedData;
bool* trapped = &trappedData;
*trapped = false;
if( !ImeUi_IsEnabled() )
return CDXUTEditBox::MsgProc( uMsg, wParam, lParam );
ImeUi_ProcessMessage( DXUTGetHWND(), uMsg, wParam, lParam, trapped );
if( *trapped == false )
CDXUTEditBox::MsgProc( uMsg, wParam, lParam );
return *trapped;
}
//--------------------------------------------------------------------------------------
void CDXUTIMEEditBox::RenderCandidateReadingWindow( float fElapsedTime, bool bReading )
{
RECT rc;
UINT nNumEntries = bReading ? 4 : MAX_CANDLIST;
D3DCOLOR TextColor, TextBkColor, SelTextColor, SelBkColor;
int nX, nXFirst, nXComp;
m_Buffer.CPtoX( m_nCaret, FALSE, &nX );
m_Buffer.CPtoX( m_nFirstVisible, FALSE, &nXFirst );
if( bReading )
{
TextColor = m_ReadingColor;
TextBkColor = m_ReadingWinColor;
SelTextColor = m_ReadingSelColor;
SelBkColor = m_ReadingSelBkColor;
}
else
{
TextColor = m_CandidateColor;
TextBkColor = m_CandidateWinColor;
SelTextColor = m_CandidateSelColor;
SelBkColor = m_CandidateSelBkColor;
}
// For Japanese IME, align the window with the first target converted character.
// For all other IMEs, align with the caret. This is because the caret
// does not move for Japanese IME.
if( GetLanguage() == MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL ) && !GetImeId() )
nXComp = 0;
else if( GetPrimaryLanguage() == LANG_JAPANESE )
s_CompString.CPtoX( s_nFirstTargetConv, FALSE, &nXComp );
else
s_CompString.CPtoX( ImeUi_GetImeCursorChars(), FALSE, &nXComp );
// Compute the size of the candidate window
int nWidthRequired = 0;
int nHeightRequired = 0;
int nSingleLineHeight = 0;
if( ( ImeUi_IsVerticalCand() && !bReading ) ||
( !ImeUi_IsHorizontalReading() && bReading ) )
{
// Vertical window
for( UINT i = 0; i < nNumEntries; ++i )
{
if( *( ImeUi_GetCandidate( i ) ) == L'\0' )
break;
SetRect( &rc, 0, 0, 0, 0 );
m_pDialog->CalcTextRect( ImeUi_GetCandidate( i ), m_Elements.GetAt( 1 ), &rc );
nWidthRequired = __max( nWidthRequired, rc.right - rc.left );
nSingleLineHeight = __max( nSingleLineHeight, rc.bottom - rc.top );
}
nHeightRequired = nSingleLineHeight * nNumEntries;
}
else
{
// Horizontal window
SetRect( &rc, 0, 0, 0, 0 );
if( bReading )
m_pDialog->CalcTextRect( s_wszReadingString, m_Elements.GetAt( 1 ), &rc );
else
{
WCHAR wszCand[256] = L"";
s_CandList.nFirstSelected = 0;
s_CandList.nHoriSelectedLen = 0;
for( UINT i = 0; i < MAX_CANDLIST; ++i )
{
if( *ImeUi_GetCandidate( i ) == L'\0' )
break;
WCHAR wszEntry[32];
swprintf_s( wszEntry, 32, L"%s ", ImeUi_GetCandidate( i ) );
// If this is the selected entry, mark its char position.
if( ImeUi_GetCandidateSelection() == i )
{
s_CandList.nFirstSelected = lstrlen( wszCand );
s_CandList.nHoriSelectedLen = lstrlen( wszEntry ) - 1; // Minus space
}
wcscat_s( wszCand, 256, wszEntry );
}
wszCand[lstrlen( wszCand ) - 1] = L'\0'; // Remove the last space
s_CandList.HoriCand.SetText( wszCand );
m_pDialog->CalcTextRect( s_CandList.HoriCand.GetBuffer(), m_Elements.GetAt( 1 ), &rc );
}
nWidthRequired = rc.right - rc.left;
nSingleLineHeight = nHeightRequired = rc.bottom - rc.top;
}
// Now that we have the dimension, calculate the location for the candidate window.
// We attempt to fit the window in this order:
// bottom, top, right, left.
bool bHasPosition = false;
// Bottom
SetRect( &rc, s_ptCompString.x + nXComp, s_ptCompString.y + m_rcText.bottom - m_rcText.top,
s_ptCompString.x + nXComp + nWidthRequired, s_ptCompString.y + m_rcText.bottom - m_rcText.top +
nHeightRequired );
// if the right edge is cut off, move it left.
if( rc.right > m_pDialog->GetWidth() )
{
rc.left -= rc.right - m_pDialog->GetWidth();
rc.right = m_pDialog->GetWidth();
}
if( rc.bottom <= m_pDialog->GetHeight() )
bHasPosition = true;
// Top
if( !bHasPosition )
{
SetRect( &rc, s_ptCompString.x + nXComp, s_ptCompString.y - nHeightRequired,
s_ptCompString.x + nXComp + nWidthRequired, s_ptCompString.y );
// if the right edge is cut off, move it left.
if( rc.right > m_pDialog->GetWidth() )
{
rc.left -= rc.right - m_pDialog->GetWidth();
rc.right = m_pDialog->GetWidth();
}
if( rc.top >= 0 )
bHasPosition = true;
}
// Right
if( !bHasPosition )
{
int nXCompTrail;
s_CompString.CPtoX( ImeUi_GetImeCursorChars(), TRUE, &nXCompTrail );
SetRect( &rc, s_ptCompString.x + nXCompTrail, 0,
s_ptCompString.x + nXCompTrail + nWidthRequired, nHeightRequired );
if( rc.right <= m_pDialog->GetWidth() )
bHasPosition = true;
}
// Left
if( !bHasPosition )
{
SetRect( &rc, s_ptCompString.x + nXComp - nWidthRequired, 0,
s_ptCompString.x + nXComp, nHeightRequired );
if( rc.right >= 0 )
bHasPosition = true;
}
if( !bHasPosition )
{
// The dialog is too small for the candidate window.
// Fall back to render at 0, 0. Some part of the window
// will be cut off.
rc.left = 0;
rc.right = nWidthRequired;
}
// If we are rendering the candidate window, save the position
// so that mouse clicks are checked properly.
if( !bReading )
s_CandList.rcCandidate = rc;
// Render the elements
m_pDialog->DrawRect( &rc, TextBkColor );
if( ( ImeUi_IsVerticalCand() && !bReading ) ||
( !ImeUi_IsHorizontalReading() && bReading ) )
{
// Vertical candidate window
for( UINT i = 0; i < nNumEntries; ++i )
{
// Here we are rendering one line at a time
rc.bottom = rc.top + nSingleLineHeight;
// Use a different color for the selected string
if( ImeUi_GetCandidateSelection() == i )
{
m_pDialog->DrawRect( &rc, SelBkColor );
m_Elements.GetAt( 1 )->FontColor.Current = SelTextColor;
}
else
m_Elements.GetAt( 1 )->FontColor.Current = TextColor;
m_pDialog->DrawText( ImeUi_GetCandidate( i ), m_Elements.GetAt( 1 ), &rc );
rc.top += nSingleLineHeight;
}
}
else
{
// Horizontal candidate window
m_Elements.GetAt( 1 )->FontColor.Current = TextColor;
if( bReading )
m_pDialog->DrawText( s_wszReadingString, m_Elements.GetAt( 1 ), &rc );
else
m_pDialog->DrawText( s_CandList.HoriCand.GetBuffer(), m_Elements.GetAt( 1 ), &rc );
// Render the selected entry differently
if( !bReading )
{
int nXLeft, nXRight;
s_CandList.HoriCand.CPtoX( s_CandList.nFirstSelected, FALSE, &nXLeft );
s_CandList.HoriCand.CPtoX( s_CandList.nFirstSelected + s_CandList.nHoriSelectedLen, FALSE, &nXRight );
rc.right = rc.left + nXRight;
rc.left += nXLeft;
m_pDialog->DrawRect( &rc, SelBkColor );
m_Elements.GetAt( 1 )->FontColor.Current = SelTextColor;
m_pDialog->DrawText( s_CandList.HoriCand.GetBuffer() + s_CandList.nFirstSelected,
m_Elements.GetAt( 1 ), &rc, false, s_CandList.nHoriSelectedLen );
}
}
}
//--------------------------------------------------------------------------------------
void CDXUTIMEEditBox::RenderComposition( float fElapsedTime )
{
s_CompString.SetText( ImeUi_GetCompositionString() );
RECT rcCaret =
{
0, 0, 0, 0
};
int nX, nXFirst;
m_Buffer.CPtoX( m_nCaret, FALSE, &nX );
m_Buffer.CPtoX( m_nFirstVisible, FALSE, &nXFirst );
CDXUTElement* pElement = m_Elements.GetAt( 1 );
// Get the required width
RECT rc =
{
m_rcText.left + nX - nXFirst, m_rcText.top,
m_rcText.left + nX - nXFirst, m_rcText.bottom
};
m_pDialog->CalcTextRect( s_CompString.GetBuffer(), pElement, &rc );
// If the composition string is too long to fit within
// the text area, move it to below the current line.
// This matches the behavior of the default IME.
if( rc.right > m_rcText.right )
OffsetRect( &rc, m_rcText.left - rc.left, rc.bottom - rc.top );
// Save the rectangle position for processing highlighted text.
RECT rcFirst = rc;
// Update s_ptCompString for RenderCandidateReadingWindow().
s_ptCompString.x = rc.left; s_ptCompString.y = rc.top;
D3DCOLOR TextColor = m_CompColor;
// Render the window and string.
// If the string is too long, we must wrap the line.
pElement->FontColor.Current = TextColor;
const WCHAR* pwszComp = s_CompString.GetBuffer();
int nCharLeft = s_CompString.GetTextSize();
for(; ; )
{
// Find the last character that can be drawn on the same line.
int nLastInLine;
int bTrail;
s_CompString.XtoCP( m_rcText.right - rc.left, &nLastInLine, &bTrail );
int nNumCharToDraw = __min( nCharLeft, nLastInLine );
m_pDialog->CalcTextRect( pwszComp, pElement, &rc, nNumCharToDraw );
// Draw the background
// For Korean IME, blink the composition window background as if it
// is a cursor.
if( GetPrimaryLanguage() == LANG_KOREAN )
{
if( m_bCaretOn )
{
m_pDialog->DrawRect( &rc, m_CompWinColor );
}
else
{
// Not drawing composition string background. We
// use the editbox's text color for composition
// string text.
TextColor = m_Elements.GetAt( 0 )->FontColor.States[DXUT_STATE_NORMAL];
}
}
else
{
// Non-Korean IME. Always draw composition background.
m_pDialog->DrawRect( &rc, m_CompWinColor );
}
// Draw the text
pElement->FontColor.Current = TextColor;
m_pDialog->DrawText( pwszComp, pElement, &rc, false, nNumCharToDraw );
// Advance pointer and counter
nCharLeft -= nNumCharToDraw;
pwszComp += nNumCharToDraw;
if( nCharLeft <= 0 )
break;
// Advance rectangle coordinates to beginning of next line
OffsetRect( &rc, m_rcText.left - rc.left, rc.bottom - rc.top );
}
// Load the rect for the first line again.
rc = rcFirst;
// Inspect each character in the comp string.
// For target-converted and target-non-converted characters,
// we display a different background color so they appear highlighted.
int nCharFirst = 0;
nXFirst = 0;
s_nFirstTargetConv = -1;
BYTE* pAttr;
const WCHAR* pcComp;
for( pcComp = s_CompString.GetBuffer(), pAttr = ImeUi_GetCompStringAttr();
*pcComp != L'\0'; ++pcComp, ++pAttr )
{
D3DCOLOR bkColor;
// Render a different background for this character
int nXLeft, nXRight;
s_CompString.CPtoX( int( pcComp - s_CompString.GetBuffer() ), FALSE, &nXLeft );
s_CompString.CPtoX( int( pcComp - s_CompString.GetBuffer() ), TRUE, &nXRight );
// Check if this character is off the right edge and should
// be wrapped to the next line.
if( nXRight - nXFirst > m_rcText.right - rc.left )
{
// Advance rectangle coordinates to beginning of next line
OffsetRect( &rc, m_rcText.left - rc.left, rc.bottom - rc.top );
// Update the line's first character information
nCharFirst = int( pcComp - s_CompString.GetBuffer() );
s_CompString.CPtoX( nCharFirst, FALSE, &nXFirst );
}
// If the caret is on this character, save the coordinates
// for drawing the caret later.
if( ImeUi_GetImeCursorChars() == ( DWORD )( pcComp - s_CompString.GetBuffer() ) )
{
rcCaret = rc;
rcCaret.left += nXLeft - nXFirst - 1;
rcCaret.right = rcCaret.left + 2;
}
// Set up color based on the character attribute
if( *pAttr == ATTR_TARGET_CONVERTED )
{
pElement->FontColor.Current = m_CompTargetColor;
bkColor = m_CompTargetBkColor;
}
else if( *pAttr == ATTR_TARGET_NOTCONVERTED )
{
pElement->FontColor.Current = m_CompTargetNonColor;
bkColor = m_CompTargetNonBkColor;
}
else
{
continue;
}
RECT rcTarget =
{
rc.left + nXLeft - nXFirst, rc.top, rc.left + nXRight - nXFirst, rc.bottom
};
m_pDialog->DrawRect( &rcTarget, bkColor );
m_pDialog->DrawText( pcComp, pElement, &rcTarget, false, 1 );
// Record the first target converted character's index
if( -1 == s_nFirstTargetConv )
s_nFirstTargetConv = int( pAttr - ImeUi_GetCompStringAttr() );
}
// Render the composition caret
if( m_bCaretOn )
{
// If the caret is at the very end, its position would not have
// been computed in the above loop. We compute it here.
if( ImeUi_GetImeCursorChars() == ( DWORD )s_CompString.GetTextSize() )
{
s_CompString.CPtoX( ImeUi_GetImeCursorChars(), FALSE, &nX );
rcCaret = rc;
rcCaret.left += nX - nXFirst - 1;
rcCaret.right = rcCaret.left + 2;
}
m_pDialog->DrawRect( &rcCaret, m_CompCaretColor );
}
}
//--------------------------------------------------------------------------------------
void CDXUTIMEEditBox::RenderIndicator( float fElapsedTime )
{
CDXUTElement* pElement = m_Elements.GetAt( 9 );
pElement->TextureColor.Blend( DXUT_STATE_NORMAL, fElapsedTime );
m_pDialog->DrawSprite( pElement, &m_rcIndicator, DXUT_NEAR_BUTTON_DEPTH );
RECT rc = m_rcIndicator;
InflateRect( &rc, -m_nSpacing, -m_nSpacing );
pElement->FontColor.Current = m_IndicatorImeColor;
RECT rcCalc =
{
0, 0, 0, 0
};
// If IME system is off, draw English indicator.
WCHAR* pwszIndicator = ImeUi_IsEnabled() ? ImeUi_GetIndicatior() : L"En";
m_pDialog->CalcTextRect( pwszIndicator, pElement, &rcCalc );
m_pDialog->DrawText( pwszIndicator, pElement, &rc );
}
//--------------------------------------------------------------------------------------
void CDXUTIMEEditBox::Render( float fElapsedTime )
{
if( m_bVisible == false )
return;
// If we have not computed the indicator symbol width,
// do it.
if( !m_nIndicatorWidth )
{
RECT rc =
{
0, 0, 0, 0
};
m_pDialog->CalcTextRect( L"En", m_Elements.GetAt( 9 ), &rc );
m_nIndicatorWidth = rc.right - rc.left;
// Update the rectangles now that we have the indicator's width
UpdateRects();
}
// Let the parent render first (edit control)
CDXUTEditBox::Render( fElapsedTime );
CDXUTElement* pElement = GetElement( 1 );
if( pElement )
{
s_CompString.SetFontNode( m_pDialog->GetFont( pElement->iFont ) );
s_CandList.HoriCand.SetFontNode( m_pDialog->GetFont( pElement->iFont ) );
}
//
// Now render the IME elements
//
ImeUi_RenderUI();
if( m_bHasFocus )
{
// Render the input locale indicator
RenderIndicator( fElapsedTime );
// Display the composition string.
// This method should also update s_ptCompString
// for RenderCandidateReadingWindow.
RenderComposition( fElapsedTime );
// Display the reading/candidate window. RenderCandidateReadingWindow()
// uses s_ptCompString to position itself. s_ptCompString must have
// been filled in by RenderComposition().
if( ImeUi_IsShowReadingWindow() )
// Reading window
RenderCandidateReadingWindow( fElapsedTime, true );
else if( ImeUi_IsShowCandListWindow() )
// Candidate list window
RenderCandidateReadingWindow( fElapsedTime, false );
}
}
//--------------------------------------------------------------------------------------
void CDXUTIMEEditBox::SetImeEnableFlag( bool bFlag )
{
s_bImeFlag = bFlag;
}
//--------------------------------------------------------------------------------------
void CDXUTIMEEditBox::Initialize( HWND hWnd )
{
ImeUiCallback_DrawRect = NULL;
ImeUiCallback_Malloc = malloc;
ImeUiCallback_Free = free;
ImeUiCallback_DrawFans = NULL;
ImeUi_Initialize( hWnd );
s_CompString.SetBufferSize( MAX_COMPSTRING_SIZE );
ImeUi_EnableIme( true );
}