From 548041be285ab2897b4b77111e8f07afd800fac9 Mon Sep 17 00:00:00 2001 From: sspanak Date: Thu, 19 Jun 2025 14:59:01 +0300 Subject: [PATCH] fixed more problems regarding Shift and automatic text case --- .../sspanak/tt9/ime/CommandHandler.java | 6 +++-- .../github/sspanak/tt9/ime/TypingHandler.java | 16 +++-------- .../io/github/sspanak/tt9/ime/UiHandler.java | 2 +- .../sspanak/tt9/ime/modes/InputMode.java | 5 ++-- .../sspanak/tt9/ime/modes/ModeIdeograms.java | 2 +- .../sspanak/tt9/ime/modes/ModeWords.java | 18 +++++++------ .../tt9/ime/modes/helpers/AutoTextCase.java | 27 +++++++------------ 7 files changed, 30 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/CommandHandler.java b/app/src/main/java/io/github/sspanak/tt9/ime/CommandHandler.java index f52f9733..6e7f1f08 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/CommandHandler.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/CommandHandler.java @@ -209,10 +209,12 @@ abstract public class CommandHandler extends TextEditingHandler { protected boolean nextTextCase() { - if (!mInputMode.nextTextCase(true)) { + if (!mInputMode.nextTextCase()) { return false; } + mInputMode.skipNextTextCaseDetection(); + // if there are no suggestions or they are special chars, we don't need to adjust their text case final String currentWord = suggestionOps.isEmpty() || mInputMode.getSequence().isEmpty() ? "" : suggestionOps.getCurrent(); if (currentWord.isEmpty() || !Character.isAlphabetic(currentWord.charAt(0))) { @@ -227,7 +229,7 @@ abstract public class CommandHandler extends TextEditingHandler { currentSuggestionIndex = suggestionOps.containsStem() ? currentSuggestionIndex - 1 : currentSuggestionIndex; for (int retries = 0; retries <= 2; retries++) { - if (!currentWord.equals(mInputMode.getSuggestions().get(currentSuggestionIndex)) || !mInputMode.nextTextCase(false)) { + if (!currentWord.equals(mInputMode.getSuggestions().get(currentSuggestionIndex)) || !mInputMode.nextTextCase()) { break; } } diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/TypingHandler.java b/app/src/main/java/io/github/sspanak/tt9/ime/TypingHandler.java index 4a9e26a4..2b3edc44 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/TypingHandler.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/TypingHandler.java @@ -146,15 +146,10 @@ public abstract class TypingHandler extends KeyPadHandler { if (settings.getBackspaceRecomposing() && repeat == 0 && noTextSelection && suggestionOps.isEmpty() && !DictionaryLoader.getInstance(this).isRunning()) { final String previousWord = mInputMode.recompose(); if (textField.recompose(previousWord)) { - mInputMode.setOnEndRecomposing(this::updateShiftState); getSuggestions(previousWord); } else { mInputMode.reset(); } - - if (!mainView.isTextEditingPaletteShown() && !mainView.isCommandPaletteShown()) { - statusBar.setText(mInputMode); - } } return true; @@ -432,7 +427,7 @@ public abstract class TypingHandler extends KeyPadHandler { String trimmedWord = suggestionOps.getCurrent(mLanguage, mInputMode.getSequenceLength()); appHacks.setComposingTextWithHighlightedStem(trimmedWord, mInputMode); - updateShiftState(false, true); + updateShiftState(mInputMode.getSuggestions().isEmpty(), true); forceShowWindow(); } @@ -445,8 +440,8 @@ public abstract class TypingHandler extends KeyPadHandler { } - protected void updateShiftState(boolean determineTextCase, boolean onlyWhenWords) { - if (onlyWhenWords && (suggestionOps.isEmpty() || !new Text(suggestionOps.getCurrent()).isAlphabetic())) { + protected void updateShiftState(boolean determineTextCase, boolean onlyWhenLetters) { + if (onlyWhenLetters && !new Text(suggestionOps.getCurrent()).isAlphabetic()) { return; } @@ -460,9 +455,4 @@ public abstract class TypingHandler extends KeyPadHandler { statusBar.setText(mInputMode); } } - - - protected void updateShiftState() { - updateShiftState(false, false); - } } diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/UiHandler.java b/app/src/main/java/io/github/sspanak/tt9/ime/UiHandler.java index 24033520..2bd869b8 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/UiHandler.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/UiHandler.java @@ -73,7 +73,7 @@ abstract class UiHandler extends AbstractHandler { return modeTextCase; } - int wordTextCase = currentWord.getTextCase(); + final int wordTextCase = currentWord.getTextCase(); return wordTextCase == InputMode.CASE_UPPER ? InputMode.CASE_CAPITALIZE : wordTextCase; } diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/modes/InputMode.java b/app/src/main/java/io/github/sspanak/tt9/ime/modes/InputMode.java index a0cb5d8a..6bfdb06b 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/modes/InputMode.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/modes/InputMode.java @@ -39,7 +39,6 @@ abstract public class InputMode { @NonNull protected String digitSequence = ""; protected final boolean isEmailMode; @NonNull protected Language language = new NullLanguage(); - @NonNull protected Runnable onEndRecomposing = () -> {}; protected final SettingsStore settings; @NonNull protected final ArrayList suggestions = new ArrayList<>(); @NonNull protected Runnable onSuggestionsUpdated = () -> {}; @@ -179,7 +178,6 @@ abstract public class InputMode { public void beforeDeleteText() {} public String recompose() { return null; } - public void setOnEndRecomposing(Runnable r) { onEndRecomposing = r != null ? r : () -> {}; } public void replaceLastLetter() {} public void reset() { @@ -208,7 +206,7 @@ abstract public class InputMode { * If "analyzeSurroundingText" is true, and when the mode supports text analyzing, it may apply * additional logic to determine the next valid text case. */ - public boolean nextTextCase(boolean analyzeSurroundingText) { + public boolean nextTextCase() { if (!language.hasUpperCase()) { return false; } @@ -220,6 +218,7 @@ abstract public class InputMode { } public void determineNextWordTextCase(int nextDigit) {} + public void skipNextTextCaseDetection() {} // Based on the internal logic of the mode (punctuation or grammar rules), re-adjust the text case for when getSuggestions() is called. protected String adjustSuggestionTextCase(String word, int newTextCase) { return word; } diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModeIdeograms.java b/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModeIdeograms.java index 9c0400d1..269238b5 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModeIdeograms.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModeIdeograms.java @@ -30,7 +30,7 @@ public class ModeIdeograms extends ModeWords { @Override protected String adjustSuggestionTextCase(String word, int newTextCase) { return word; } @Override public void determineNextWordTextCase(int nextDigit) {} - @Override public boolean nextTextCase(boolean analyzeSurroundingText) { return false; } + @Override public boolean nextTextCase() { return false; } @Override diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModeWords.java b/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModeWords.java index d14b0fb0..5034606a 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModeWords.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModeWords.java @@ -160,10 +160,10 @@ class ModeWords extends ModeCheonjiin { if (isRecomposing) { isRecomposing = false; textCase = settings.getTextCase(); - onEndRecomposing.run(); } } + @Override public void reset() { basicReset(); @@ -382,14 +382,12 @@ class ModeWords extends ModeCheonjiin { } @Override - public boolean nextTextCase(boolean analyzeSurroundingText) { - boolean changed = super.nextTextCase(analyzeSurroundingText); + public boolean nextTextCase() { + // convert any internally used text cases to user-friendly, because this is what the user + // is allowed to choose from. + textCase = getTextCase(); - // "analyzeSurroundingText" is true only when Shift is pressed to avoid expensive - // calls to getStringBeforeCursor(). - if (analyzeSurroundingText && textCase == CASE_LOWER && autoTextCase.isLowerCaseForbidden(language, textField.getStringBeforeCursor())) { - changed = super.nextTextCase(true); - } + boolean changed = super.nextTextCase(); // since it's a user's choice, the default matters no more textFieldTextCase = changed ? CASE_UNDEFINED : textFieldTextCase; @@ -397,6 +395,10 @@ class ModeWords extends ModeCheonjiin { return changed; } + @Override + public void skipNextTextCaseDetection() { + autoTextCase.skipNext(); + } @Override public boolean shouldReplaceLastLetter(int n, boolean h) { diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/modes/helpers/AutoTextCase.java b/app/src/main/java/io/github/sspanak/tt9/ime/modes/helpers/AutoTextCase.java index 71268a4c..bacf4147 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/modes/helpers/AutoTextCase.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/modes/helpers/AutoTextCase.java @@ -13,12 +13,14 @@ public class AutoTextCase { @NonNull private final Sequences sequences; @NonNull private final SettingsStore settings; private final boolean isUs; + private boolean skipNext; public AutoTextCase(@NonNull SettingsStore settingsStore, @NonNull Sequences sequences, @Nullable InputType inputType) { this.sequences = sequences; settings = settingsStore; isUs = inputType != null && inputType.isUs(); + skipNext = false; } /** @@ -59,6 +61,11 @@ public class AutoTextCase { return currentTextCase; } + if (skipNext) { + skipNext = false; + return textFieldTextCase != InputMode.CASE_UNDEFINED ? textFieldTextCase : currentTextCase; + } + // lowercase also takes priority but not as strict as uppercase if (textFieldTextCase != InputMode.CASE_UNDEFINED && currentTextCase != InputMode.CASE_LOWER) { return textFieldTextCase; @@ -90,23 +97,7 @@ public class AutoTextCase { } - /** - * Prevent lowercase at the beginning of a line when the respective setting is enabled, - * or at the beginning of a sentence, when auto capitalization is enabled. - */ - public boolean isLowerCaseForbidden(@NonNull Language language, @NonNull String beforeCursor) { - if (!language.hasUpperCase()) { - return false; - } - - if (settings.getAutoTextCase() && (beforeCursor.isEmpty() || Text.isStartOfSentence(beforeCursor))) { - return true; - } - - if (settings.getAutoCapitalsAfterNewline() && beforeCursor.endsWith("\n")) { - return true; - } - - return false; + public void skipNext() { + skipNext = true; } }