aboutsummaryrefslogtreecommitdiff
path: root/samples/DX_APIUsage/DXUT/Optional/DXUTguiIME.cpp
diff options
context:
space:
mode:
authorDave Clark <[email protected]>2018-02-28 17:22:22 -0500
committerDave Clark <[email protected]>2018-02-28 17:22:22 -0500
commit25528fd230f5f4298c35123a833cdb112675808e (patch)
treef5aca3f5ee5a7734df41e7b974a04c37ddff528e /samples/DX_APIUsage/DXUT/Optional/DXUTguiIME.cpp
parentPush GfeSDK #173 (diff)
downloadgfesdk-25528fd230f5f4298c35123a833cdb112675808e.tar.xz
gfesdk-25528fd230f5f4298c35123a833cdb112675808e.zip
Push SDK # 1.1.186
Documentation updates.
Diffstat (limited to 'samples/DX_APIUsage/DXUT/Optional/DXUTguiIME.cpp')
-rw-r--r--samples/DX_APIUsage/DXUT/Optional/DXUTguiIME.cpp990
1 files changed, 990 insertions, 0 deletions
diff --git a/samples/DX_APIUsage/DXUT/Optional/DXUTguiIME.cpp b/samples/DX_APIUsage/DXUT/Optional/DXUTguiIME.cpp
new file mode 100644
index 0000000..4bb0596
--- /dev/null
+++ b/samples/DX_APIUsage/DXUT/Optional/DXUTguiIME.cpp
@@ -0,0 +1,990 @@
+//--------------------------------------------------------------------------------------
+// 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 );
+}
+
+