mirror of
https://gitlab.futo.org/keyboard/latinime.git
synced 2024-09-28 14:54:30 +01:00
Add undo, redo, paste actions
This commit is contained in:
parent
9cccdcb79d
commit
e2999ada34
20
java/res/drawable/clipboard.xml
Normal file
20
java/res/drawable/clipboard.xml
Normal 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>
|
20
java/res/drawable/redo.xml
Normal file
20
java/res/drawable/redo.xml
Normal 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>
|
20
java/res/drawable/undo.xml
Normal file
20
java/res/drawable/undo.xml
Normal 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>
|
@ -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>
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -29,6 +29,9 @@ interface KeyboardManagerForAction {
|
||||
fun triggerSystemVoiceInput()
|
||||
|
||||
fun updateTheme(newTheme: ThemeOption)
|
||||
|
||||
fun sendCodePointEvent(codePoint: Int)
|
||||
fun sendKeyEvent(keyCode: Int, metaState: Int)
|
||||
}
|
||||
|
||||
interface ActionWindow {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
)
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user