v1.2.4: native EditText input bridge, proper IME support
This commit is contained in:
@@ -4,13 +4,17 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import android.text.TextWatcher
|
||||
import android.widget.EditText
|
||||
import android.view.KeyEvent as AndroidKeyEvent
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.CheckCircle
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
@@ -425,6 +429,7 @@ fun SshTerminalScreen(
|
||||
var showForwardDialog by remember { mutableStateOf(false) }
|
||||
val context = androidx.compose.ui.platform.LocalContext.current
|
||||
val currentFontSize = remember { mutableStateOf(SshConfigStorage.loadFontSize(context)) }
|
||||
val editTextRef = remember { mutableStateOf<EditText?>(null) }
|
||||
|
||||
val terminalFontSize = fontMap[currentFontSize.value] ?: 12
|
||||
val terminalFontSizeSp = terminalFontSize.sp
|
||||
@@ -469,12 +474,25 @@ fun SshTerminalScreen(
|
||||
}
|
||||
}
|
||||
|
||||
val focusModifier = remember { FocusRequester() }
|
||||
|
||||
// 连接后自动获取焦点
|
||||
// 连接后自动让 EditText 获取焦点(弹出软键盘)
|
||||
LaunchedEffect(state.connected) {
|
||||
if (state.connected) {
|
||||
focusModifier.requestFocus()
|
||||
// 延迟一点确保 EditText 已创建
|
||||
kotlinx.coroutines.delay(200)
|
||||
editTextRef.value?.let { et ->
|
||||
et.requestFocus()
|
||||
val imm = context.getSystemService(android.content.Context.INPUT_METHOD_SERVICE) as? android.view.inputmethod.InputMethodManager
|
||||
imm?.showSoftInput(et, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 点击终端区域让 EditText 重新获取焦点
|
||||
val requestEditTextFocus: () -> Unit = {
|
||||
editTextRef.value?.let { et ->
|
||||
et.requestFocus()
|
||||
val imm = context.getSystemService(android.content.Context.INPUT_METHOD_SERVICE) as? android.view.inputmethod.InputMethodManager
|
||||
imm?.showSoftInput(et, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,30 +501,65 @@ fun SshTerminalScreen(
|
||||
.fillMaxSize()
|
||||
.background(bg)
|
||||
.onKeyEvent { handleKeyEvent(it) }
|
||||
.focusRequester(focusModifier)
|
||||
.clickable { focusModifier.requestFocus() }
|
||||
) {
|
||||
// 透明输入框 — 极小的 1x30 dp,仅用来弹出软键盘
|
||||
BasicTextField(
|
||||
value = TextFieldValue(currentInput),
|
||||
onValueChange = { newVal ->
|
||||
currentInput = newVal.text
|
||||
if (newVal.text.endsWith('\n')) {
|
||||
val cmd = newVal.text.dropLast(1).trim()
|
||||
if (cmd.isNotEmpty()) {
|
||||
coroutineScope.launch {
|
||||
manager.sendCommand(cmd)
|
||||
// 原生 EditText 输入桥——任何输入法都能正确识别
|
||||
AndroidView(
|
||||
factory = { ctx ->
|
||||
EditText(ctx).apply {
|
||||
setTextColor(0x00000000) // 透明文字
|
||||
setBackgroundColor(0x00000000) // 透明背景
|
||||
textSize = 1f // 极小的字号
|
||||
val lp = android.view.ViewGroup.LayoutParams(1, 30)
|
||||
layoutParams = lp
|
||||
// IME 选项:Done 动作(回车即发送)
|
||||
imeOptions = EditorInfo.IME_ACTION_SEND or EditorInfo.IME_FLAG_NO_EXTRACT_UI
|
||||
setSingleLine()
|
||||
// 文本变化监听
|
||||
addTextChangedListener(object : android.text.TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun afterTextChanged(s: android.text.Editable?) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||
val text = s?.toString() ?: ""
|
||||
// 检测回车
|
||||
val nlIdx = text.indexOf('\n')
|
||||
if (nlIdx >= 0) {
|
||||
val cmd = text.substring(0, nlIdx)
|
||||
if (cmd.isNotEmpty()) {
|
||||
coroutineScope.launch {
|
||||
manager.sendCommand(cmd)
|
||||
}
|
||||
}
|
||||
currentInput = ""
|
||||
post { setText("") }
|
||||
} else {
|
||||
currentInput = text
|
||||
}
|
||||
}
|
||||
})
|
||||
// 硬件键盘回车
|
||||
setOnKeyListener { _, keyCode, event ->
|
||||
if (event.action == android.view.KeyEvent.ACTION_DOWN && keyCode == AndroidKeyEvent.KEYCODE_ENTER) {
|
||||
val cmd = currentInput.trim()
|
||||
if (cmd.isNotEmpty()) {
|
||||
coroutineScope.launch {
|
||||
manager.sendCommand(cmd)
|
||||
}
|
||||
}
|
||||
currentInput = ""
|
||||
post { setText("") }
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
currentInput = ""
|
||||
// IDE 编辑器标记:用于焦点请求
|
||||
tag = "terminal_input"
|
||||
editTextRef.value = this
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.width(1.dp)
|
||||
.height(30.dp)
|
||||
.focusRequester(focusModifier),
|
||||
singleLine = true,
|
||||
textStyle = TextStyle(color = Color.Transparent, fontSize = 1.sp)
|
||||
)
|
||||
|
||||
Column(
|
||||
@@ -515,6 +568,7 @@ fun SshTerminalScreen(
|
||||
.systemBarsPadding()
|
||||
.imePadding()
|
||||
.navigationBarsPadding()
|
||||
.clickable { requestEditTextFocus() }
|
||||
) {
|
||||
// ── 终端顶栏 ──
|
||||
Surface(
|
||||
|
||||
Reference in New Issue
Block a user