package com.rizoma.trazo.ime import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import com.rizoma.trazo.model.Layouts /** Puente con el editor real. Lo implementa el InputMethodService. */ interface ImeHost { fun commit(text: String) fun backspace() fun newline() fun textBeforeCursor(n: Int): CharSequence /** TextUtils.CAP_MODE_* combinado: !=0 si el editor quiere mayúscula aquí. */ fun cursorCapsMode(): Int } enum class ShiftState { OFF, ON, LOCK } enum class Layer { LETTERS, SYMBOLS } /** * Toda la lógica de escritura. La UI solo dispara eventos; el estado de mayúscula * y de capa son observables por Compose. */ class KeyboardController(private val host: ImeHost) { var shift by mutableStateOf(ShiftState.ON) private set var layer by mutableStateOf(Layer.LETTERS) private set val keys get() = if (layer == Layer.LETTERS) Layouts.letters else Layouts.symbols val shifted get() = shift != ShiftState.OFF fun onChar(raw: Char) { val out = if (shifted) raw.uppercaseChar() else raw host.commit(out.toString()) if (shift == ShiftState.ON) shift = ShiftState.OFF // mayúscula de un solo disparo } fun onSpace() { // Doble espacio tras palabra → ". " (puntua y capitaliza). val before = host.textBeforeCursor(2) if (before.length == 2 && before[1] == ' ' && before[0].isLetterOrDigit()) { host.backspace() host.commit(". ") } else { host.commit(" ") } syncAutoShift() } fun onBackspace() { host.backspace() syncAutoShift() } fun onEnter() { host.newline() syncAutoShift() } fun onShift() { shift = when (shift) { ShiftState.OFF -> ShiftState.ON ShiftState.ON -> ShiftState.LOCK ShiftState.LOCK -> ShiftState.OFF } } fun toggleLayer() { layer = if (layer == Layer.LETTERS) Layer.SYMBOLS else Layer.LETTERS } /** Recalcula la mayúscula automática según el contexto del editor. */ fun syncAutoShift() { if (shift == ShiftState.LOCK) return shift = if (host.cursorCapsMode() != 0) ShiftState.ON else ShiftState.OFF } }