diff options
| author | Dave Clark <[email protected]> | 2018-02-28 17:22:22 -0500 |
|---|---|---|
| committer | Dave Clark <[email protected]> | 2018-02-28 17:22:22 -0500 |
| commit | 25528fd230f5f4298c35123a833cdb112675808e (patch) | |
| tree | f5aca3f5ee5a7734df41e7b974a04c37ddff528e /samples/DX_APIUsage/DXUT/Optional/DXUTguiIME.cpp | |
| parent | Push GfeSDK #173 (diff) | |
| download | gfesdk-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.cpp | 990 |
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 ); +} + + |