diff --git a/java/res/drawable/clipboard.xml b/java/res/drawable/clipboard.xml
new file mode 100644
index 000000000..e4cf2e78d
--- /dev/null
+++ b/java/res/drawable/clipboard.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/java/res/drawable/redo.xml b/java/res/drawable/redo.xml
new file mode 100644
index 000000000..290274cdb
--- /dev/null
+++ b/java/res/drawable/redo.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/java/res/drawable/undo.xml b/java/res/drawable/undo.xml
new file mode 100644
index 000000000..8ad21710b
--- /dev/null
+++ b/java/res/drawable/undo.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/java/res/values/strings-uix.xml b/java/res/values/strings-uix.xml
index 579c94cef..ebd092013 100644
--- a/java/res/values/strings-uix.xml
+++ b/java/res/values/strings-uix.xml
@@ -2,6 +2,9 @@
Voice Input
Theme Switcher
+ Paste from Clipboard
+ Undo
+ Redo
AMOLED Dark Purple
AOSP Material Dark
diff --git a/java/src/org/futo/inputmethod/latin/LatinIME.kt b/java/src/org/futo/inputmethod/latin/LatinIME.kt
index 10243c55c..c0e8827e0 100644
--- a/java/src/org/futo/inputmethod/latin/LatinIME.kt
+++ b/java/src/org/futo/inputmethod/latin/LatinIME.kt
@@ -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)
diff --git a/java/src/org/futo/inputmethod/latin/inputlogic/InputLogic.java b/java/src/org/futo/inputmethod/latin/inputlogic/InputLogic.java
index 41ff7f60b..96a3fcf11 100644
--- a/java/src/org/futo/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/org/futo/inputmethod/latin/inputlogic/InputLogic.java
@@ -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);
}
diff --git a/java/src/org/futo/inputmethod/latin/uix/Action.kt b/java/src/org/futo/inputmethod/latin/uix/Action.kt
index aa21b1bfd..20b606963 100644
--- a/java/src/org/futo/inputmethod/latin/uix/Action.kt
+++ b/java/src/org/futo/inputmethod/latin/uix/Action.kt
@@ -29,6 +29,9 @@ interface KeyboardManagerForAction {
fun triggerSystemVoiceInput()
fun updateTheme(newTheme: ThemeOption)
+
+ fun sendCodePointEvent(codePoint: Int)
+ fun sendKeyEvent(keyCode: Int, metaState: Int)
}
interface ActionWindow {
diff --git a/java/src/org/futo/inputmethod/latin/uix/ActionBar.kt b/java/src/org/futo/inputmethod/latin/uix/ActionBar.kt
index df22b8249..8ffbe8a81 100644
--- a/java/src/org/futo/inputmethod/latin/uix/ActionBar.kt
+++ b/java/src/org/futo/inputmethod/latin/uix/ActionBar.kt
@@ -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)
+ }
}
}
}
diff --git a/java/src/org/futo/inputmethod/latin/uix/actions/ClipboardAction.kt b/java/src/org/futo/inputmethod/latin/uix/actions/ClipboardAction.kt
new file mode 100644
index 000000000..07ee7f3f1
--- /dev/null
+++ b/java/src/org/futo/inputmethod/latin/uix/actions/ClipboardAction.kt
@@ -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,
+)
\ No newline at end of file
diff --git a/java/src/org/futo/inputmethod/latin/uix/actions/EmojiAction.kt b/java/src/org/futo/inputmethod/latin/uix/actions/EmojiAction.kt
index d23637fda..8cf36d093 100644
--- a/java/src/org/futo/inputmethod/latin/uix/actions/EmojiAction.kt
+++ b/java/src/org/futo/inputmethod/latin/uix/actions/EmojiAction.kt
@@ -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)
}
}
diff --git a/java/src/org/futo/inputmethod/latin/uix/actions/UndoRedoActions.kt b/java/src/org/futo/inputmethod/latin/uix/actions/UndoRedoActions.kt
new file mode 100644
index 000000000..09742728a
--- /dev/null
+++ b/java/src/org/futo/inputmethod/latin/uix/actions/UndoRedoActions.kt
@@ -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,
+)
\ No newline at end of file