コード例 #1
0
void FWindowsTextInputMethodSystem::OnIMEActivationStateChanged(const bool bIsEnabled)
{
	if(bIsEnabled)
	{
		// It seems that switching away from an IMM based IME doesn't generate a deactivation notification
		//check(CurrentAPI == EAPI::Unknown);

		const HKL KeyboardLayout = ::GetKeyboardLayout(0);

		TF_INPUTPROCESSORPROFILE TSFProfile;
		if(SUCCEEDED(TSFInputProcessorProfileManager->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &TSFProfile)) && TSFProfile.dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR)
		{
			CurrentAPI = EAPI::TSF;
		}
		else if(::ImmGetIMEFileName(KeyboardLayout, nullptr, 0) > 0)
		{
			CurrentAPI = EAPI::IMM;
			UpdateIMMProperty(KeyboardLayout);
		}
		else
		{
			CurrentAPI = EAPI::Unknown;
		}
	}
	else
	{
		// It seems that switching away from an IMM based IME doesn't generate a deactivation notification
		//check(CurrentAPI != EAPI::Unknown);

		CurrentAPI = EAPI::Unknown;
	}

	LogActiveIMEInfo();
}
コード例 #2
0
void FWindowsTextInputMethodSystem::OnIMEActivationStateChanged(const bool bIsEnabled)
{
	if(bIsEnabled)
	{
		// It seems that switching away from an IMM based IME doesn't generate a deactivation notification
		//check(CurrentAPI == EAPI::Unknown);

		FString APIString;
		const HKL KeyboardLayout = ::GetKeyboardLayout(0);
		if(::ImmGetIMEFileName(KeyboardLayout, nullptr, 0) > 0)
		{
			CurrentAPI = EAPI::IMM;
			UpdateIMMProperty(KeyboardLayout);
			APIString = TEXT("IMM");
		}
		else
		{
			CurrentAPI = EAPI::TSF;
			APIString = TEXT("TSF");
		}
		UE_LOG(LogWindowsTextInputMethodSystem, Verbose, TEXT("IME system now activated using %s."), *APIString);
	}
	else
	{
		// It seems that switching away from an IMM based IME doesn't generate a deactivation notification
		//check(CurrentAPI != EAPI::Unknown);

		CurrentAPI = EAPI::Unknown;
		UE_LOG(LogWindowsTextInputMethodSystem, Verbose, TEXT("IME system now deactivated."));
	}
}
コード例 #3
0
bool FWindowsTextInputMethodSystem::InitializeIMM()
{
	UE_LOG(LogWindowsTextInputMethodSystem, Verbose, TEXT("Initializing IMM..."));

	IMMContextId = ::ImmCreateContext();
	UpdateIMMProperty(::GetKeyboardLayout(0));

	UE_LOG(LogWindowsTextInputMethodSystem, Verbose, TEXT("Initialized IMM!"));

	return true;
}
コード例 #4
0
int32 FWindowsTextInputMethodSystem::ProcessMessage(HWND hwnd, uint32 msg, WPARAM wParam, LPARAM lParam)
{
	if(CurrentAPI != EAPI::IMM)
	{
		return DefWindowProc(hwnd, msg, wParam, lParam);
	}

	switch(msg)
	{
	case WM_INPUTLANGCHANGEREQUEST:
	case WM_INPUTLANGCHANGE:
		{
			HKL KeyboardLayoutHandle = reinterpret_cast<HKL>(lParam);

			UpdateIMMProperty(KeyboardLayoutHandle);

			return DefWindowProc(hwnd, msg, wParam, lParam);
		}
		break;
	case WM_IME_SETCONTEXT:
		{
			if(ActiveContext.IsValid())
			{
				// Disable showing an IME-implemented composition window if we're going to draw the composition string ourselves.
				if(wParam && ShouldDrawIMMCompositionString())
				{
					lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
				}

				UE_LOG(LogWindowsTextInputMethodSystem, Verbose, TEXT("Setting IMM context."));
			}

			return DefWindowProc(hwnd, msg, wParam, lParam);
		}
		break;
	case WM_IME_NOTIFY:
		{
			return DefWindowProc(hwnd, msg, wParam, lParam);
		}
		break;
	case WM_IME_REQUEST:
		{
			return DefWindowProc(hwnd, msg, wParam, lParam);
		}
		break;
	case WM_IME_STARTCOMPOSITION:
		{
			if(ActiveContext.IsValid())
			{
				BeginIMMComposition();
				
				UE_LOG(LogWindowsTextInputMethodSystem, Verbose, TEXT("Beginning IMM composition."));
			}

			return DefWindowProc(hwnd, msg, wParam, lParam);
		}
		break;
	case WM_IME_COMPOSITION:
		{
			if(ActiveContext.IsValid())
			{
				FInternalContext& InternalContext = ContextToInternalContextMap[ActiveContext];
				check(InternalContext.IMMContext.IsComposing);

				HIMC IMMContext = ::ImmGetContext(hwnd);

				UpdateIMMWindowPositions(IMMContext);

				const bool bHasBeenCanceled = !lParam;
				const bool bHasCompositionStringFlag = !!(lParam & GCS_COMPSTR);
				const bool bHasResultStringFlag = !!(lParam & GCS_RESULTSTR);
				const bool bHasNoMoveCaretFlag = !!(lParam & CS_NOMOVECARET);
				const bool bHasCursorPosFlag = !!(lParam & GCS_CURSORPOS);

				// Canceled, so remove the compositing string
				if(bHasBeenCanceled)
				{
					CancelIMMComposition();
				}

				// Check Result
				if(bHasResultStringFlag)
				{
					// If we're being deactivated, so we need to take the current selection so we can restore it properly
					// otherwise calling SetTextInRange can cause the cursor/selection to jump around
					uint32 SelectionBeginIndex = 0;
					uint32 SelectionLength = 0;
					ITextInputMethodContext::ECaretPosition SelectionCaretPosition = ITextInputMethodContext::ECaretPosition::Ending;
					ActiveContext->GetSelectionRange(SelectionBeginIndex, SelectionLength, SelectionCaretPosition);

					const FString ResultString = GetIMMStringAsFString(IMMContext, GCS_RESULTSTR);
					UE_LOG(LogWindowsTextInputMethodSystem, Verbose, TEXT("WM_IME_COMPOSITION Result String: %s"), *ResultString);

					// Update Result
					ActiveContext->SetTextInRange(InternalContext.IMMContext.CompositionBeginIndex, InternalContext.IMMContext.CompositionLength, ResultString);

					if(InternalContext.IMMContext.IsDeactivating)
					{
						// Restore any previous selection
						ActiveContext->SetSelectionRange(SelectionBeginIndex, SelectionLength, SelectionCaretPosition);
					}
					else
					{
						// Once we get a result, we're done; set the caret to the end of the result and end the current composition
						ActiveContext->SetSelectionRange(InternalContext.IMMContext.CompositionBeginIndex + ResultString.Len(), 0, ITextInputMethodContext::ECaretPosition::Ending);
					}

					EndIMMComposition();
				}

				// Check Composition
				if(bHasCompositionStringFlag)
				{
					const FString CompositionString = GetIMMStringAsFString(IMMContext, GCS_COMPSTR);
					UE_LOG(LogWindowsTextInputMethodSystem, Verbose, TEXT("WM_IME_COMPOSITION Composition String: %s"), *CompositionString);

					// Not all IMEs send a cancel request when you press escape, but instead just set the string to empty
					// We need to cancel out the composition string here to avoid weirdness when you start typing again
					// Don't do this if we have a result string, as we'll have already called EndIMMComposition to finish the composition
					if(CompositionString.Len() == 0 && !bHasResultStringFlag)
					{
						CancelIMMComposition();
					}

					// We've typed a character, so we need to clear out any currently selected text to mimic what happens when you normally type into a text input
					uint32 SelectionBeginIndex = 0;
					uint32 SelectionLength = 0;
					ITextInputMethodContext::ECaretPosition SelectionCaretPosition = ITextInputMethodContext::ECaretPosition::Ending;
					ActiveContext->GetSelectionRange(SelectionBeginIndex, SelectionLength, SelectionCaretPosition);
					if(SelectionLength)
					{
						ActiveContext->SetTextInRange(SelectionBeginIndex, SelectionLength, TEXT(""));
					}

					// If we received a result (handled above) then the previous composition will have been ended, so we need to start a new one now
					// This ensures that each composition ends up as its own distinct undo
					if(!InternalContext.IMMContext.IsComposing)
					{
						BeginIMMComposition();
					}

					const int32 CurrentCompositionBeginIndex = InternalContext.IMMContext.CompositionBeginIndex;
					const uint32 CurrentCompositionLength = InternalContext.IMMContext.CompositionLength;

					// Update Composition Range
					InternalContext.IMMContext.CompositionLength = CompositionString.Len();
					ActiveContext->UpdateCompositionRange(InternalContext.IMMContext.CompositionBeginIndex, InternalContext.IMMContext.CompositionLength);

					// Update Composition
					ActiveContext->SetTextInRange(CurrentCompositionBeginIndex, CurrentCompositionLength, CompositionString);
				}

				// Check Cursor
				if(!bHasNoMoveCaretFlag && bHasCursorPosFlag)
				{
					const LONG CursorPositionResult = ::ImmGetCompositionString(IMMContext, GCS_CURSORPOS, nullptr, 0);
					const int16 CursorPosition = CursorPositionResult & 0xFFFF;

					// Update Cursor
					UE_LOG(LogWindowsTextInputMethodSystem, Verbose, TEXT("WM_IME_COMPOSITION Cursor Position: %d"), CursorPosition);
					ActiveContext->SetSelectionRange(InternalContext.IMMContext.CompositionBeginIndex + CursorPosition, 0, ITextInputMethodContext::ECaretPosition::Ending);
				}

				::ImmReleaseContext(hwnd, IMMContext);

				UE_LOG(LogWindowsTextInputMethodSystem, Verbose, TEXT("Updating IMM composition."));
			}

			return DefWindowProc(hwnd, msg, wParam, lParam);
		}
		break;
	case WM_IME_ENDCOMPOSITION:
		{
			// On composition end, notify context of the end.
			if(ActiveContext.IsValid())
			{
				EndIMMComposition();
				
				UE_LOG(LogWindowsTextInputMethodSystem, Verbose, TEXT("Ending IMM composition."));
			}

			return DefWindowProc(hwnd, msg, wParam, lParam);
		}
		break;
	case WM_IME_CHAR:
		{
			// Suppress sending a WM_CHAR for this WM_IME_CHAR - the composition windows messages will have handled this.
			UE_LOG(LogWindowsTextInputMethodSystem, Verbose, TEXT("Ignoring WM_IME_CHAR message."));
			return 0;
		}
		break;
	default:
		{
			UE_LOG(LogWindowsTextInputMethodSystem, Warning, TEXT("Unexpected windows message received for processing."));

			return DefWindowProc(hwnd, msg, wParam, lParam);
		}
		break;
	}
}