From 86e815a142c8aa13213151e381a8a24ef23073d3 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Tue, 14 Jun 2011 16:28:57 +0900 Subject: [PATCH] Implement expandable candidates pane This change removes horizontal scroll from candidates strip. Instead of that this change introduces "fixed 3 items candidates strip" and "expandable candidates pane". Bug: 4175031 Change-Id: Ia367d9074436fdea76d3b653d81798ce2749170e --- .../btn_close_candidates_pane.9.png | Bin 0 -> 1110 bytes .../btn_expand_candidates_pane.9.png | Bin 0 -> 1123 bytes .../btn_close_candidates_pane.9.png | Bin 0 -> 713 bytes .../btn_expand_candidates_pane.9.png | Bin 0 -> 681 bytes java/res/layout/candidates_strip.xml | 61 ++++++ java/res/layout/input_view.xml | 45 +++-- .../keyboard/KeyboardSwitcher.java | 6 +- .../inputmethod/latin/CandidateView.java | 176 ++++++++++++++++-- .../android/inputmethod/latin/LatinIME.java | 2 +- .../android/inputmethod/latin/Suggest.java | 2 +- 10 files changed, 251 insertions(+), 41 deletions(-) create mode 100644 java/res/drawable-hdpi/btn_close_candidates_pane.9.png create mode 100644 java/res/drawable-hdpi/btn_expand_candidates_pane.9.png create mode 100644 java/res/drawable-mdpi/btn_close_candidates_pane.9.png create mode 100644 java/res/drawable-mdpi/btn_expand_candidates_pane.9.png create mode 100644 java/res/layout/candidates_strip.xml diff --git a/java/res/drawable-hdpi/btn_close_candidates_pane.9.png b/java/res/drawable-hdpi/btn_close_candidates_pane.9.png new file mode 100644 index 0000000000000000000000000000000000000000..6df00f2294b61f183ea3e648ad771dbc4825352e GIT binary patch literal 1110 zcmV-c1gZOpP)vV(<__RfKp1D zZEPdx%v5IOzs3C?@9@0y?m73pC%t(8&AIp7^Zozdcka9QeCN9lNdTgAA&;S>gXp4* zF1qMYSUbrjtpt|%=@m1(*eP{T<7M0_VFQvjOR7kkmNX@4tE8S1 zJUwnZo6ESAZXvA=gpW#ETS7RG^Jp+J52sS33a$zS$0e;!6l}!3+BvHev?|3s zRcx;d9h20X=4upwuX9gJ+M8mUA~N6t(~`ENyBV#sCD0itw#dvDC1||p?T^5r1jl-$ zB%MRR4|SS_h!T+0M{ThiGqb2$DQOw-J}@fj#uyvt{@72~NqQj0E_SA#^!6e!8Drot zf-#^U7zFl9x-C*~!nGh>&FcL@_ane3F)s<%sMKjUL~I~^ z-p&*al=L`o5V!;Q3)l_316&2}0uD-gG(xkg)z@&8qc9Lp#j9EDxq`7nzcpf-IwyE0zyd`OI z9(Ak_5EsaNaJ(Yt`n$j;Kb`i${X@WcVbu4L*YaWQ9 ziGBS}d)uG4?=Z8UB6+Ss-m@g>24G{Ic0 z0^LSnlv+Y}4%kmxfo_D_3O)zyGP7^XxYLS~>6>J`z$(I$=~>`+@4p%7CBJ0)4Oz+b zd|6joRm2`3D-?8Q5&IgpPXDa1AbYm6yU9;4xA8+y7hQDGMF--4@i!-vgjvJocuZr~m)} literal 0 HcmV?d00001 diff --git a/java/res/drawable-hdpi/btn_expand_candidates_pane.9.png b/java/res/drawable-hdpi/btn_expand_candidates_pane.9.png new file mode 100644 index 0000000000000000000000000000000000000000..63015ec5bbd67239e9ac423c755806e59dadc97d GIT binary patch literal 1123 zcmV-p1f2VcP)LH zCn9ML@G@|Xi%tP=0biKeY>90T@F4IsaEptlffvl|Se0-p*np&Ul4_DBB~3_rLek<2 zeipm!g!`&VT302cbPH)!Kzu~fstUw;K92-9=I&G%se~&6!o!kQCK49;Ug`D(bp5efwb_F zZ~*wefwL4*iAd^WZnGOTv$l1W?t7n~_Di}m#xC}WJml*cU@S4VDmdy~knK06h)ra* zuQfCKqw!)hZ!bwVHp-l%*1%ne*hKn#J)RRtKk%`nkw{;4JCpP*@R6iDa#ZSEeL2_W zp)?UM#p_k<$sCnufTh3|NjqY8cu7mh%9|(2YOu{YYIUw<9z!8wp2*d~@!6c~+sFz; zfJcGXB{i1se?!uBz}vvEi;e<2a#W`mh#?m-iTqh~(adImt-xUy-4E>Z%J?7fP4qqB z9v6K8Ja1+fb6kgS#=;mNE|Hnw_}V-LGyC1lb^>pD0KLHbVI3yvKHy#8X5bR=ikZD; zW(x%wa-Ay+vNRDhJIh=$x0dLR0xtoxzzx7Vl7=Kb0=(hDT>zdlvkyzC>s&KtHeIMm z{99|v*L%wp&Fm9k3)%N+3Ggbg6Sy2W4Qw*A@d(a3*HodFBQ}vqUk75|N@n&gFhbUB zEg~Co`~ZxY*@-fHNtXd@8@LM*o5-iWE(eBU49x5l@D%VJ@HMb8p_Ls1uJ_~5BW>a! zX|GqL`=$66SyE3*_vjnte&^a7YnD>|)}7u3JRWOgX0v8?DS_uP=Lk7l3rTzYPK~v( zu^MxZJt?NCL}9CYFtHtt@qO@LX?2}296lUs#gp+TaVQu*qz*JHkAsGTanQJ=4NZ)L zHUuB1nxx@!%lWRF8PKf-b}?7bog^y;QwDUKncKn70o%>&t198NVr2RvFc65tk?Bvs zd6!>FR)gh@Our->nf_8WlvWL~2gwEnojJsw=dII!R#?z`*4Zuf(~E8Vp{I*3y6B<< p@xS<+lS#s?a69=gBw8Gx#9ylpY%_ild}06q002ovPDHLkV1h{^|G5AF literal 0 HcmV?d00001 diff --git a/java/res/drawable-mdpi/btn_close_candidates_pane.9.png b/java/res/drawable-mdpi/btn_close_candidates_pane.9.png new file mode 100644 index 0000000000000000000000000000000000000000..5ea56925dd62dc57218671ba37c00519e59efe6a GIT binary patch literal 713 zcmV;)0yh1LP)500004b3#c}2nYxW zdv*Ld&k%}q)i@-83bp7VZBa`?V;Zjb~ZS}Qq6%R$;|E3+(XqNFZh53n6r53B$m zfLCVrJ&R@CFp`SENub~mwORq@fxBilwwg3~rLEtO!5O@Z>1%3e=fL*|T;4rWm zcnn-Lv(IU^NDI<0>As|SNn?^aSBdZRxOva(Zwid09!b-Z9!V;sqW_ay@VseBJ3W4 z6yvwxegj!x`h0yC0a5}k`LmLl-3Cg)GH_Yai3q={&yFlG9lrimBHw5X_z0BEYze^3 z7J;+C7vQ|4V>NzNpN&~yD!%4IAV-07z#?#__GUG+WuOc!0T*gYRy_n2Vw42)-q)R> z&t4K-@u$t~pYl1-8-kKO1zv|9@Qk3!G{M#`AlDI}N!=n)2Q2OYpq v8hblvtF27>Z~mV#Nti_*s500004b3#c}2nYxW zdQ)GspBz;i| zIogm0eno*a6b(&n{Q(X(J2q6%ra@V@WLq@3Xao{D6r$qV9JDkQ1VK{B)!=PCyu6>! z`-%}?nvdk(T6`;j|R-bA$(I0u{p4gy=i7vN(`=|>*R znqkxt;1)0(5QVV?tN|}mN~^m9qaFqB0GEO9z&l_C_yqh04gqI@i@+7219%VIPbqz? zW{YY;y43}BO}(wQRM5ACZY}b<8v>)YtMlrzI$Ra~zue)-n^)T#0W+npsspuvWby|h zf2uARbwu4z$Mb+>e8$6Pq#l^Z>Ra_loiNSeGgD4>po~E)Fa#{6lz!F&lTz9QmVm+V zEgB_ZfIhbC$a)6wn0mLIVdy&5dum5!dOiI5^1z&l@pEQw2)GW+sV7T-^Z;|fHQ+*K zX~*Yu9+;*W|30FW(re%u&)|&m<3J%Pf|*+3i5Ui!6vnaIuyroti<|_s2>aJP4$hsqFybb9}B;sQd^dTX^V0` zSw=G$6?P+z?)fs>$?$6{B`FCL29IKs!bg?W^r{ca-9OdVnMyFD4X(hWxnaU+I{q8| zIYIg(f4Y*i8nf)JUDIV5mt^B^?J(_87vmRBRN2o=gnls!Va07~>GZnT?({BEeW&+< z?N0A&Lt!#dd*1^Wfy%xeGy%V;@9khe_9IvPH{WNHN=ii!)owu+-%R-fAJysIiKm53 P00000NkvXXu0mjfeu65S literal 0 HcmV?d00001 diff --git a/java/res/layout/candidates_strip.xml b/java/res/layout/candidates_strip.xml new file mode 100644 index 000000000..296ea7585 --- /dev/null +++ b/java/res/layout/candidates_strip.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + diff --git a/java/res/layout/input_view.xml b/java/res/layout/input_view.xml index 5da1a4826..52b5eccc6 100644 --- a/java/res/layout/input_view.xml +++ b/java/res/layout/input_view.xml @@ -32,32 +32,47 @@ android:id="@+id/candidates_container" android:orientation="horizontal" android:layout_width="match_parent" - android:layout_height="wrap_content" - style="?attr/suggestionsStripBackgroundStyle" + android:layout_height="@dimen/candidate_strip_minimum_height" + android:gravity="bottom" > - - - + + + + + + + mWords = new ArrayList(); private final ArrayList mDividers = new ArrayList(); private final int mCandidatePadding; + private final int mCandidateStripHeight; private final boolean mConfigCandidateHighlightFontColorEnabled; private final CharacterStyle mInvertedForegroundColorSpan; private final CharacterStyle mInvertedBackgroundColorSpan; @@ -132,8 +142,10 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo super(context, attrs); Resources res = context.getResources(); - mPreviewPopup = new PopupWindow(context); LayoutInflater inflater = LayoutInflater.from(context); + inflater.inflate(R.layout.candidates_strip, this); + + mPreviewPopup = new PopupWindow(context); mPreviewText = (TextView) inflater.inflate(R.layout.candidate_preview, null); mPreviewPopup.setWindowLayoutMode(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); @@ -148,8 +160,26 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord); mCandidatePadding = res.getDimensionPixelOffset(R.dimen.candidate_padding); + mCandidateStripHeight = res.getDimensionPixelOffset(R.dimen.candidate_strip_height); for (int i = 0; i < MAX_SUGGESTIONS; i++) { - final TextView tv = (TextView)inflater.inflate(R.layout.candidate, null); + final TextView tv; + switch (i) { + case 0: + tv = (TextView)findViewById(R.id.candidate_left); + tv.setPadding(mCandidatePadding, 0, 0, 0); + break; + case 1: + tv = (TextView)findViewById(R.id.candidate_center); + break; + case 2: + tv = (TextView)findViewById(R.id.candidate_right); + break; + default: + tv = (TextView)inflater.inflate(R.layout.candidate, null); + break; + } + if (i < NUM_CANDIDATES_IN_STRIP) + setLayoutWeight(tv, 1.0f); tv.setTag(i); tv.setOnClickListener(this); if (i == 0) @@ -157,19 +187,38 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo mWords.add(tv); if (i > 0) { final View divider = inflater.inflate(R.layout.candidate_divider, null); + divider.measure(UNSPECIFIED_MEASURESPEC, UNSPECIFIED_MEASURESPEC); mDividers.add(divider); } } - scrollTo(0, getScrollY()); + mExpandCandidatesPane = findViewById(R.id.expand_candidates_pane); + mExpandCandidatesPane.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + expandCandidatesPane(); + } + }); + mCloseCandidatesPane = findViewById(R.id.close_candidates_pane); + mCloseCandidatesPane.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + closeCandidatesPane(); + } + }); } /** * A connection back to the input method. * @param listener */ - public void setListener(Listener listener) { + public void setListener(Listener listener, View inputView) { mListener = listener; + mKeyboardView = inputView.findViewById(R.id.keyboard_view); + mCandidatesPane = (ViewGroup)inputView.findViewById(R.id.candidates_pane); + mCandidatesPane.setOnClickListener(this); + mCandidatesPaneContainer = (ViewGroup)inputView.findViewById( + R.id.candidates_pane_container); } public void setSuggestions(SuggestedWords suggestions) { @@ -183,6 +232,15 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo } } + private static void setLayoutWeight(View v, float weight) { + ViewGroup.LayoutParams lp = v.getLayoutParams(); + if (lp instanceof LinearLayout.LayoutParams) { + LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp; + llp.width = 0; + llp.weight = weight; + } + } + private CharSequence getStyledCandidateWord(CharSequence word, boolean isAutoCorrect) { if (!isAutoCorrect) return word; @@ -216,7 +274,14 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo final List suggestedWordInfoList = suggestions.mSuggestedWordInfoList; clear(); + final int paneWidth = getWidth(); + final int dividerWidth = mDividers.get(0).getMeasuredWidth(); + int x = 0; + int y = 0; + int fromIndex = NUM_CANDIDATES_IN_STRIP; final int count = Math.min(mWords.size(), suggestions.size()); + closeCandidatesPane(); + mExpandCandidatesPane.setEnabled(count >= NUM_CANDIDATES_IN_STRIP); for (int i = 0; i < count; i++) { final CharSequence word = suggestions.getWord(i); if (word == null) continue; @@ -233,33 +298,91 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo final boolean isPunctuationSuggestions = (word.length() == 1 && count > 1); final TextView tv = mWords.get(i); + // TODO: Reorder candidates in strip as appropriate. The center candidate should hold + // the word when space is typed (valid typed word or auto corrected word). tv.setTextColor(getCandidateTextColor(isAutoCorrect, isSuggestedCandidate || isPunctuationSuggestions, info)); tv.setText(getStyledCandidateWord(word, isAutoCorrect)); - if (i == 0) { - tv.setPadding(mCandidatePadding, 0, 0, 0); - } else if (i == count - 1) { - tv.setPadding(0, 0, mCandidatePadding, 0); - } else { - tv.setPadding(0, 0, 0, 0); + // TODO: call TextView.setTextScaleX() to fit the candidate in single line. + if (i >= NUM_CANDIDATES_IN_STRIP) { + tv.measure(UNSPECIFIED_MEASURESPEC, UNSPECIFIED_MEASURESPEC); + final int width = tv.getMeasuredWidth(); + // TODO: Handle overflow case. + if (dividerWidth + x + width >= paneWidth) { + centeringCandidates(fromIndex, i - 1, x, paneWidth); + x = 0; + y += mCandidateStripHeight; + fromIndex = i; + } + if (x != 0) { + final View divider = mDividers.get(i - NUM_CANDIDATES_IN_STRIP); + mCandidatesPane.addView(divider); + placeCandidateAt(divider, x, y); + x += dividerWidth; + } + mCandidatesPane.addView(tv); + placeCandidateAt(tv, x, y); + x += width; } - if (i > 0) - addView(mDividers.get(i - 1)); - addView(tv); if (DBG && info != null) { final TextView dv = new TextView(getContext(), null); dv.setTextSize(10.0f); dv.setTextColor(0xff808080); dv.setText(info.getDebugString()); - addView(dv); + // TODO: debug view for candidate strip needed. + mCandidatesPane.addView(dv); LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)dv.getLayoutParams(); lp.gravity = Gravity.BOTTOM; } } + if (x != 0) { + // Centering last candidates row. + centeringCandidates(fromIndex, count - 1, x, paneWidth); + } + } - scrollTo(0, getScrollY()); - requestLayout(); + private void placeCandidateAt(View v, int x, int y) { + ViewGroup.LayoutParams lp = v.getLayoutParams(); + if (lp instanceof ViewGroup.MarginLayoutParams) { + ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)lp; + mlp.width = v.getMeasuredWidth(); + mlp.height = v.getMeasuredHeight(); + mlp.setMargins(x, y + (mCandidateStripHeight - mlp.height) / 2, 0, 0); + } + } + + private void centeringCandidates(int from, int to, int width, int paneWidth) { + final ViewGroup pane = mCandidatesPane; + final int fromIndex = pane.indexOfChild(mWords.get(from)); + final int toIndex = pane.indexOfChild(mWords.get(to)); + final int offset = (paneWidth - width) / 2; + for (int index = fromIndex; index <= toIndex; index++) { + offsetMargin(pane.getChildAt(index), offset, 0); + } + } + + private static void offsetMargin(View v, int dx, int dy) { + ViewGroup.LayoutParams lp = v.getLayoutParams(); + if (lp instanceof ViewGroup.MarginLayoutParams) { + ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)lp; + mlp.setMargins(mlp.leftMargin + dx, mlp.topMargin + dy, 0, 0); + } + } + + private void expandCandidatesPane() { + mExpandCandidatesPane.setVisibility(View.GONE); + mCloseCandidatesPane.setVisibility(View.VISIBLE); + mCandidatesPaneContainer.setMinimumHeight(mKeyboardView.getMeasuredHeight()); + mCandidatesPaneContainer.setVisibility(View.VISIBLE); + mKeyboardView.setVisibility(View.GONE); + } + + private void closeCandidatesPane() { + mExpandCandidatesPane.setVisibility(View.VISIBLE); + mCloseCandidatesPane.setVisibility(View.GONE); + mCandidatesPaneContainer.setVisibility(View.GONE); + mKeyboardView.setVisibility(View.VISIBLE); } public void onAutoCorrectionInverted(CharSequence autoCorrectedWord) { @@ -310,7 +433,9 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo public void clear() { mShowingAddToDictionary = false; mShowingAutoCorrectionInverted = false; - removeAllViews(); + for (int i = 0; i < NUM_CANDIDATES_IN_STRIP; i++) + mWords.get(i).setText(null); + mCandidatesPane.removeAllViews(); } private void hidePreview() { @@ -349,9 +474,13 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo @Override public boolean onLongClick(View view) { - final int index = (Integer) view.getTag(); + final Object tag = view.getTag(); + if (!(tag instanceof Integer)) + return true; + final int index = (Integer) tag; if (index >= mSuggestions.size()) return true; + final CharSequence word = mSuggestions.getWord(index); if (word.length() < 2) return false; @@ -361,15 +490,22 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo @Override public void onClick(View view) { - final int index = (Integer) view.getTag(); + final Object tag = view.getTag(); + if (!(tag instanceof Integer)) + return; + final int index = (Integer) tag; if (index >= mSuggestions.size()) return; + final CharSequence word = mSuggestions.getWord(index); if (mShowingAddToDictionary && index == 0) { addToDictionary(word); } else { mListener.pickSuggestionManually(index, word); } + // Because some punctuation letters are not treated as word separator depending on locale, + // {@link #setSuggestions} might not be called and candidates pane left opened. + closeCandidatesPane(); } @Override diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 940f6b835..75bca9937 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -502,7 +502,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar super.setInputView(view); mCandidateViewContainer = view.findViewById(R.id.candidates_container); mCandidateView = (CandidateView) view.findViewById(R.id.candidates); - mCandidateView.setListener(this); + mCandidateView.setListener(this, view); mCandidateStripHeight = (int)mResources.getDimension(R.dimen.candidate_strip_height); } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 33f9820cc..62788fb9e 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -84,7 +84,7 @@ public class Suggest implements Dictionary.WordCallback { private final Map mUnigramDictionaries = new HashMap(); private final Map mBigramDictionaries = new HashMap(); - private int mPrefMaxSuggestions = 12; + private int mPrefMaxSuggestions = 18; private static final int PREF_MAX_BIGRAMS = 60;