aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/org/kelar/inputmethod/event/CombinerChain.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/org/kelar/inputmethod/event/CombinerChain.java')
-rw-r--r--java/src/org/kelar/inputmethod/event/CombinerChain.java137
1 files changed, 137 insertions, 0 deletions
diff --git a/java/src/org/kelar/inputmethod/event/CombinerChain.java b/java/src/org/kelar/inputmethod/event/CombinerChain.java
new file mode 100644
index 000000000..afd992e62
--- /dev/null
+++ b/java/src/org/kelar/inputmethod/event/CombinerChain.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.kelar.inputmethod.event;
+
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+
+import org.kelar.inputmethod.latin.common.Constants;
+
+import java.util.ArrayList;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This class implements the logic chain between receiving events and generating code points.
+ *
+ * Event sources are multiple. It may be a hardware keyboard, a D-PAD, a software keyboard,
+ * or any exotic input source.
+ * This class will orchestrate the composing chain that starts with an event as its input. Each
+ * composer will be given turns one after the other.
+ * The output is composed of two sequences of code points: the first, representing the already
+ * finished combining part, will be shown normally as the composing string, while the second is
+ * feedback on the composing state and will typically be shown with different styling such as
+ * a colored background.
+ */
+public class CombinerChain {
+ // The already combined text, as described above
+ private StringBuilder mCombinedText;
+ // The feedback on the composing state, as described above
+ private SpannableStringBuilder mStateFeedback;
+ private final ArrayList<Combiner> mCombiners;
+
+ /**
+ * Create an combiner chain.
+ *
+ * The combiner chain takes events as inputs and outputs code points and combining state.
+ * For example, if the input language is Japanese, the combining chain will typically perform
+ * kana conversion. This takes a string for initial text, taken to be present before the
+ * cursor: we'll start after this.
+ *
+ * @param initialText The text that has already been combined so far.
+ */
+ public CombinerChain(final String initialText) {
+ mCombiners = new ArrayList<>();
+ // The dead key combiner is always active, and always first
+ mCombiners.add(new DeadKeyCombiner());
+ mCombinedText = new StringBuilder(initialText);
+ mStateFeedback = new SpannableStringBuilder();
+ }
+
+ public void reset() {
+ mCombinedText.setLength(0);
+ mStateFeedback.clear();
+ for (final Combiner c : mCombiners) {
+ c.reset();
+ }
+ }
+
+ private void updateStateFeedback() {
+ mStateFeedback.clear();
+ for (int i = mCombiners.size() - 1; i >= 0; --i) {
+ mStateFeedback.append(mCombiners.get(i).getCombiningStateFeedback());
+ }
+ }
+
+ /**
+ * Process an event through the combining chain, and return a processed event to apply.
+ * @param previousEvents the list of previous events in this composition
+ * @param newEvent the new event to process
+ * @return the processed event. It may be the same event, or a consumed event, or a completely
+ * new event. However it may never be null.
+ */
+ @Nonnull
+ public Event processEvent(final ArrayList<Event> previousEvents,
+ @Nonnull final Event newEvent) {
+ final ArrayList<Event> modifiablePreviousEvents = new ArrayList<>(previousEvents);
+ Event event = newEvent;
+ for (final Combiner combiner : mCombiners) {
+ // A combiner can never return more than one event; it can return several
+ // code points, but they should be encapsulated within one event.
+ event = combiner.processEvent(modifiablePreviousEvents, event);
+ if (event.isConsumed()) {
+ // If the event is consumed, then we don't pass it to subsequent combiners:
+ // they should not see it at all.
+ break;
+ }
+ }
+ updateStateFeedback();
+ return event;
+ }
+
+ /**
+ * Apply a processed event.
+ * @param event the event to be applied
+ */
+ public void applyProcessedEvent(final Event event) {
+ if (null != event) {
+ // TODO: figure out the generic way of doing this
+ if (Constants.CODE_DELETE == event.mKeyCode) {
+ final int length = mCombinedText.length();
+ if (length > 0) {
+ final int lastCodePoint = mCombinedText.codePointBefore(length);
+ mCombinedText.delete(length - Character.charCount(lastCodePoint), length);
+ }
+ } else {
+ final CharSequence textToCommit = event.getTextToCommit();
+ if (!TextUtils.isEmpty(textToCommit)) {
+ mCombinedText.append(textToCommit);
+ }
+ }
+ }
+ updateStateFeedback();
+ }
+
+ /**
+ * Get the char sequence that should be displayed as the composing word. It may include
+ * styling spans.
+ */
+ public CharSequence getComposingWordWithCombiningFeedback() {
+ final SpannableStringBuilder s = new SpannableStringBuilder(mCombinedText);
+ return s.append(mStateFeedback);
+ }
+}