Add undo, redo, paste actions

This commit is contained in:
Aleksandras Kostarevas 2024-01-07 16:20:20 +02:00
parent 9cccdcb79d
commit e2999ada34
11 changed files with 142 additions and 19 deletions

View File

@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M16,4h2a2,2 0,0 1,2 2v14a2,2 0,0 1,-2 2H6a2,2 0,0 1,-2 -2V6a2,2 0,0 1,2 -2h2"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#FFFFFF"
android:strokeLineCap="round"/>
<path
android:pathData="M9,2L15,2A1,1 0,0 1,16 3L16,5A1,1 0,0 1,15 6L9,6A1,1 0,0 1,8 5L8,3A1,1 0,0 1,9 2z"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#FFFFFF"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M15,14l5,-5l-5,-5"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
<path
android:pathData="M4,20v-7a4,4 0,0 1,4 -4h12"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M9,14l-5,-5l5,-5"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
<path
android:pathData="M20,20v-7a4,4 0,0 0,-4 -4H4"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
</vector>

View File

@ -2,6 +2,9 @@
<resources>
<string name="voice_input_action_title">Voice Input</string>
<string name="theme_switcher_action_title">Theme Switcher</string>
<string name="clipboard_action_title">Paste from Clipboard</string>
<string name="undo_action_title">Undo</string>
<string name="redo_action_title">Redo</string>
<string name="amoled_dark_theme_name">AMOLED Dark Purple</string>
<string name="classic_material_dark_theme_name">AOSP Material Dark</string>

View File

@ -5,6 +5,8 @@ import android.content.res.Configuration
import android.inputmethodservice.InputMethodService
import android.os.Build
import android.os.Bundle
import android.os.SystemClock
import android.view.KeyCharacterMap
import android.view.KeyEvent
import android.view.View
import android.view.inputmethod.CompletionInfo
@ -277,7 +279,9 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
@Composable
private fun LegacyKeyboardView(hidden: Boolean) {
val modifier = if(hidden) {
Modifier.clipToBounds().size(0.dp)
Modifier
.clipToBounds()
.size(0.dp)
} else {
Modifier.onSizeChanged {
inputViewHeight = it.height
@ -703,6 +707,15 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
}
}
override fun sendCodePointEvent(codePoint: Int) {
latinIMELegacy.onCodeInput(codePoint, NOT_A_COORDINATE, NOT_A_COORDINATE, false)
}
override fun sendKeyEvent(keyCode: Int, metaState: Int) {
latinIMELegacy.mInputLogic.sendDownUpKeyEvent(keyCode, metaState)
}
@RequiresApi(Build.VERSION_CODES.R)
override fun onCreateInlineSuggestionsRequest(uiExtras: Bundle): InlineSuggestionsRequest {
return createInlineSuggestionsRequest(this, this.activeColorScheme)

View File

@ -1132,7 +1132,7 @@ public final class InputLogic {
// As for the case where we don't know the cursor position, it can happen
// because of bugs in the framework. But the framework should know, so the next
// best thing is to leave it to whatever it thinks is best.
sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL);
sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL, 0);
int totalDeletedLength = 1;
if (mDeleteCount > Constants.DELETE_ACCELERATE_AT) {
// If this is an accelerated (i.e., double) deletion, then we need to
@ -1140,7 +1140,7 @@ public final class InputLogic {
// the previous word, and will lose it after next deletion.
hasUnlearnedWordBeingDeleted |= unlearnWordBeingDeleted(
inputTransaction.mSettingsValues, currentKeyboardScriptId);
sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL);
sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL, 0);
totalDeletedLength++;
}
StatsUtils.onBackspacePressed(totalDeletedLength);
@ -2021,13 +2021,13 @@ public final class InputLogic {
*
* @param keyCode the key code to send inside the key event.
*/
private void sendDownUpKeyEvent(final int keyCode) {
public void sendDownUpKeyEvent(final int keyCode, final int metaState) {
final long eventTime = SystemClock.uptimeMillis();
mConnection.sendKeyEvent(new KeyEvent(eventTime, eventTime,
KeyEvent.ACTION_DOWN, keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.ACTION_DOWN, keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
mConnection.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
KeyEvent.ACTION_UP, keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.ACTION_UP, keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
}
@ -2045,7 +2045,7 @@ public final class InputLogic {
// TODO: Remove this special handling of digit letters.
// For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}.
if (codePoint >= '0' && codePoint <= '9') {
sendDownUpKeyEvent(codePoint - '0' + KeyEvent.KEYCODE_0);
sendDownUpKeyEvent(codePoint - '0' + KeyEvent.KEYCODE_0, 0);
return;
}
@ -2055,7 +2055,7 @@ public final class InputLogic {
// a hardware keyboard event on pressing enter or delete. This is bad for many
// reasons (there are race conditions with commits) but some applications are
// relying on this behavior so we continue to support it for older apps.
sendDownUpKeyEvent(KeyEvent.KEYCODE_ENTER);
sendDownUpKeyEvent(KeyEvent.KEYCODE_ENTER, 0);
} else {
mConnection.commitText(StringUtils.newSingleCodePointString(codePoint), 1);
}

View File

@ -29,6 +29,9 @@ interface KeyboardManagerForAction {
fun triggerSystemVoiceInput()
fun updateTheme(newTheme: ThemeOption)
fun sendCodePointEvent(codePoint: Int)
fun sendKeyEvent(keyCode: Int, metaState: Int)
}
interface ActionWindow {

View File

@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.Icon
@ -60,8 +61,11 @@ import org.futo.inputmethod.latin.SuggestedWords
import org.futo.inputmethod.latin.SuggestedWords.SuggestedWordInfo
import org.futo.inputmethod.latin.SuggestedWords.SuggestedWordInfo.KIND_TYPED
import org.futo.inputmethod.latin.suggestions.SuggestionStripView
import org.futo.inputmethod.latin.uix.actions.ClipboardAction
import org.futo.inputmethod.latin.uix.actions.EmojiAction
import org.futo.inputmethod.latin.uix.actions.RedoAction
import org.futo.inputmethod.latin.uix.actions.ThemeAction
import org.futo.inputmethod.latin.uix.actions.UndoAction
import org.futo.inputmethod.latin.uix.actions.VoiceInputAction
import org.futo.inputmethod.latin.uix.theme.DarkColorScheme
import org.futo.inputmethod.latin.uix.theme.UixThemeWrapper
@ -287,7 +291,7 @@ fun ActionItem(action: Action, onSelect: (Action) -> Unit) {
cornerRadius = CornerRadius(radius, radius)
)
}
.width(64.dp)
.width(50.dp)
.fillMaxHeight(),
colors = IconButtonDefaults.iconButtonColors(contentColor = contentCol)
) {
@ -317,12 +321,9 @@ fun RowScope.ActionItems(onSelect: (Action) -> Unit) {
ActionItem(EmojiAction, onSelect)
ActionItem(VoiceInputAction, onSelect)
ActionItem(ThemeAction, onSelect)
Box(modifier = Modifier
.fillMaxHeight()
.weight(1.0f)) {
}
ActionItem(UndoAction, onSelect)
ActionItem(RedoAction, onSelect)
ActionItem(ClipboardAction, onSelect)
}
@ -386,7 +387,11 @@ fun ActionBar(
ExpandActionsButton(isActionsOpen.value) { isActionsOpen.value = !isActionsOpen.value }
if(isActionsOpen.value) {
ActionItems(onActionActivated)
LazyRow {
item {
ActionItems(onActionActivated)
}
}
} else if(inlineSuggestions.isNotEmpty() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
InlineSuggestions(inlineSuggestions)
} else if(words != null) {
@ -399,7 +404,9 @@ fun ActionBar(
Spacer(modifier = Modifier.weight(1.0f))
}
ActionItemSmall(VoiceInputAction, onActionActivated)
if(!isActionsOpen.value) {
ActionItemSmall(VoiceInputAction, onActionActivated)
}
}
}
}

View File

@ -0,0 +1,14 @@
package org.futo.inputmethod.latin.uix.actions
import android.view.KeyEvent
import org.futo.inputmethod.latin.R
import org.futo.inputmethod.latin.uix.Action
val ClipboardAction = Action(
icon = R.drawable.clipboard,
name = R.string.clipboard_action_title,
simplePressImpl = { manager, _ ->
manager.sendKeyEvent(KeyEvent.KEYCODE_V, KeyEvent.META_CTRL_ON)
},
windowImpl = null,
)

View File

@ -55,6 +55,7 @@ import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import org.futo.inputmethod.latin.R
import org.futo.inputmethod.latin.common.Constants
import org.futo.inputmethod.latin.uix.Action
import org.futo.inputmethod.latin.uix.ActionWindow
import org.futo.inputmethod.latin.uix.PersistentActionState
@ -293,9 +294,9 @@ val EmojiAction = Action(
}, onExit = {
manager.closeActionWindow()
}, onSpace = {
manager.typeText(" ")
manager.sendCodePointEvent(Constants.CODE_SPACE)
}, onBackspace = {
manager.backspace(1)
manager.sendCodePointEvent(Constants.CODE_DELETE)
}, bitmaps = state.bitmaps, emojis = emojis)
}
}

View File

@ -0,0 +1,22 @@
package org.futo.inputmethod.latin.uix.actions
import android.view.KeyEvent
import org.futo.inputmethod.latin.R
import org.futo.inputmethod.latin.uix.Action
val UndoAction = Action(
icon = R.drawable.undo,
name = R.string.undo_action_title,
simplePressImpl = { manager, _ ->
manager.sendKeyEvent(KeyEvent.KEYCODE_Z, KeyEvent.META_CTRL_ON)
},
windowImpl = null,
)
val RedoAction = Action(
icon = R.drawable.redo,
name = R.string.redo_action_title,
simplePressImpl = { manager, _ ->
manager.sendKeyEvent(KeyEvent.KEYCODE_Y, KeyEvent.META_CTRL_ON)
},
windowImpl = null,
)