diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
new file mode 100644
index 0000000..d536a1e
--- /dev/null
+++ b/.github/workflows/android.yml
@@ -0,0 +1,26 @@
+name: Android CI
+
+on:
+ push:
+ branches: [ "main" ]
+ pull_request:
+ branches: [ "main" ]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: set up JDK 17
+ uses: actions/setup-java@v3
+ with:
+ java-version: '17'
+ distribution: 'temurin'
+ cache: gradle
+
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+ - name: Build with Gradle
+ run: ./gradlew build
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 6b03294..8951dd1 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -4,8 +4,10 @@
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index fdf8d99..e805548 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 7232108..db20ece 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -31,13 +31,13 @@ android {
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
- jvmTarget = '11'
+ jvmTarget = '17'
}
buildFeatures {
compose true
}
composeOptions {
- kotlinCompilerExtensionVersion = "1.5.1"
+ kotlinCompilerExtensionVersion = "1.5.4"
}
packagingOptions {
resources {
diff --git a/app/src/main/java/jp/kaleidot725/sample/ui/composable/Demo.kt b/app/src/main/java/jp/kaleidot725/sample/ui/composable/Demo.kt
index c398a88..d4f601d 100644
--- a/app/src/main/java/jp/kaleidot725/sample/ui/composable/Demo.kt
+++ b/app/src/main/java/jp/kaleidot725/sample/ui/composable/Demo.kt
@@ -32,6 +32,7 @@ import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@@ -49,7 +50,15 @@ fun Demo(text: String) {
val clipboardManager = LocalClipboardManager.current
val keyboardController = LocalSoftwareKeyboardController.current
- var textEditorState by remember { mutableStateOf(TextEditorState.create(text)) }
+ var textEditorState by remember {
+ mutableStateOf(
+ TextEditorState.create(
+ text,
+ TextStyle(color = Color.Black),
+ TextStyle(color = Color.Green)
+ )
+ )
+ }
val bottomPadding = if (textEditorState.isMultipleSelectionMode) 100.dp else 0.dp
val contentBottomPaddingValue =
with(LocalDensity.current) { WindowInsets.ime.getBottom(this).toDp() }
diff --git a/build.gradle b/build.gradle
index 0639d3a..4d5f57c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,15 +3,15 @@ buildscript {
core_ktx_version = '1.10.1'
lifecycle_ktx_version = '2.6.1'
activity_compose_version = '1.7.2'
- compose_ui_version = '1.5.0'
+ compose_ui_version = '1.5.4'
kotest_version = '5.6.2'
}
}
plugins {
- id 'com.android.application' version '7.4.2' apply false
- id 'com.android.library' version '7.4.2' apply false
- id 'org.jetbrains.kotlin.android' version '1.9.0' apply false
+ id 'com.android.application' version '8.1.2' apply false
+ id 'com.android.library' version '8.1.2' apply false
+ id 'org.jetbrains.kotlin.android' version '1.9.20' apply false
id 'org.jlleitschuh.gradle.ktlint' version '10.3.0'
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 8049c68..da1db5f 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/jitpack.yml b/jitpack.yml
index 46c8529..1e41e00 100644
--- a/jitpack.yml
+++ b/jitpack.yml
@@ -1,2 +1,2 @@
jdk:
- - openjdk11
\ No newline at end of file
+ - openjdk17
\ No newline at end of file
diff --git a/texteditor/build.gradle b/texteditor/build.gradle
index 51c5e22..a263936 100644
--- a/texteditor/build.gradle
+++ b/texteditor/build.gradle
@@ -31,7 +31,7 @@ android {
}
kotlinOptions {
- jvmTarget = '11'
+ jvmTarget = '17'
}
android.testOptions {
@@ -45,7 +45,13 @@ android {
}
composeOptions {
- kotlinCompilerExtensionVersion '1.5.1'
+ kotlinCompilerExtensionVersion '1.5.4'
+ }
+ publishing{
+ singleVariant('release') {
+ withSourcesJar()
+ withJavadocJar()
+ }
}
}
@@ -64,7 +70,7 @@ afterEvaluate {
from components.release
groupId = 'com.github.kaleidot725'
artifactId = 'text-editor-compose'
- version = '0.6.0'
+ version = '0.6.1'
}
}
}
diff --git a/texteditor/src/main/java/jp/kaleidot725/texteditor/controller/EditorController.kt b/texteditor/src/main/java/jp/kaleidot725/texteditor/controller/EditorController.kt
index 3a05f3c..1d272b5 100644
--- a/texteditor/src/main/java/jp/kaleidot725/texteditor/controller/EditorController.kt
+++ b/texteditor/src/main/java/jp/kaleidot725/texteditor/controller/EditorController.kt
@@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.TextFieldValue
import jp.kaleidot725.texteditor.state.TextEditorState
import jp.kaleidot725.texteditor.state.TextFieldState
@@ -12,7 +13,7 @@ import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
internal class EditorController(
- textEditorState: TextEditorState
+ val textEditorState: TextEditorState
) {
private var onChanged: (TextEditorState) -> Unit = {}
@@ -26,7 +27,7 @@ internal class EditorController(
val selectedIndices get() = _selectedIndices.toList()
private val state: TextEditorState
- get() = TextEditorState(fields, selectedIndices, isMultipleSelectionMode)
+ get() = TextEditorState(fields, selectedIndices, isMultipleSelectionMode, textEditorState.textStyle, textEditorState.textSelectedStyle)
private val lock = ReentrantLock()
@@ -62,7 +63,7 @@ internal class EditorController(
val newSplitFieldValues = splitFieldValues.subList(1, splitFieldValues.count())
val newSplitFieldStates =
- newSplitFieldValues.map { TextFieldState(value = it, isSelected = false) }
+ newSplitFieldValues.map { TextFieldState(value = it, isSelected = false, textStyle = textEditorState.textStyle, textSelectedStyle = textEditorState.textSelectedStyle) }
_fields.addAll(targetIndex + 1, newSplitFieldStates)
val lastNewSplitFieldIndex = targetIndex + newSplitFieldValues.count()
@@ -95,7 +96,7 @@ internal class EditorController(
_fields[targetIndex] = firstState
val secondValue = TextFieldValue(second, TextRange.Zero)
- val secondState = TextFieldState(value = secondValue, isSelected = false)
+ val secondState = TextFieldState(value = secondValue, isSelected = false, textStyle = textEditorState.textStyle, textSelectedStyle = textEditorState.textSelectedStyle)
_fields.add(targetIndex + 1, secondState)
selectFieldInternal(targetIndex + 1)
@@ -214,7 +215,7 @@ internal class EditorController(
fun deleteAllLine() {
lock.withLock {
_fields.clear()
- _fields.addAll(emptyList().createInitTextFieldStates())
+ _fields.addAll(emptyList().createInitTextFieldStates(textEditorState.textStyle, textEditorState.textSelectedStyle))
_selectedIndices.clear()
selectFieldInternal(0)
onChanged(state)
@@ -302,12 +303,14 @@ internal class EditorController(
}
companion object {
- fun List.createInitTextFieldStates(): List {
- if (this.isEmpty()) return listOf(TextFieldState(isSelected = false))
+ fun List.createInitTextFieldStates(textStyle: TextStyle, textSelectedStyle: TextStyle): List {
+ if (this.isEmpty()) return listOf(TextFieldState(isSelected = false, textStyle = textStyle, textSelectedStyle = textSelectedStyle))
return this.mapIndexed { _, s ->
TextFieldState(
value = TextFieldValue(s, TextRange.Zero),
- isSelected = false
+ isSelected = false,
+ textStyle = textStyle,
+ textSelectedStyle = textSelectedStyle
)
}
}
diff --git a/texteditor/src/main/java/jp/kaleidot725/texteditor/state/TextEditorState.kt b/texteditor/src/main/java/jp/kaleidot725/texteditor/state/TextEditorState.kt
index f1bb35d..640dcbd 100644
--- a/texteditor/src/main/java/jp/kaleidot725/texteditor/state/TextEditorState.kt
+++ b/texteditor/src/main/java/jp/kaleidot725/texteditor/state/TextEditorState.kt
@@ -1,6 +1,7 @@
package jp.kaleidot725.texteditor.state
import androidx.compose.runtime.Immutable
+import androidx.compose.ui.text.TextStyle
import jp.kaleidot725.texteditor.controller.EditorController.Companion.createInitTextFieldStates
@Immutable
@@ -8,6 +9,8 @@ data class TextEditorState(
val fields: List,
val selectedIndices: List,
val isMultipleSelectionMode: Boolean,
+ val textStyle: TextStyle,
+ val textSelectedStyle: TextStyle
) {
fun getAllText(): String {
return fields.map { it.value.text }.foldIndexed("") { index, acc, s ->
@@ -24,19 +27,23 @@ data class TextEditorState(
}
companion object {
- fun create(text: String): TextEditorState {
+ fun create(text: String, textStyle: TextStyle = TextStyle(), textSelectedStyle: TextStyle = TextStyle()): TextEditorState {
return TextEditorState(
- fields = text.lines().createInitTextFieldStates(),
+ fields = text.lines().createInitTextFieldStates(textStyle, textSelectedStyle),
selectedIndices = listOf(-1),
- isMultipleSelectionMode = false
+ isMultipleSelectionMode = false,
+ textStyle = textStyle,
+ textSelectedStyle = textSelectedStyle
)
}
- fun create(lines: List): TextEditorState {
+ fun create(lines: List, textStyle: TextStyle = TextStyle(), textSelectedStyle: TextStyle = TextStyle()): TextEditorState {
return TextEditorState(
- fields = lines.createInitTextFieldStates(),
+ fields = lines.createInitTextFieldStates(textStyle, textSelectedStyle),
selectedIndices = listOf(-1),
- isMultipleSelectionMode = false
+ isMultipleSelectionMode = false,
+ textStyle = textStyle,
+ textSelectedStyle = textSelectedStyle
)
}
}
diff --git a/texteditor/src/main/java/jp/kaleidot725/texteditor/state/TextFieldState.kt b/texteditor/src/main/java/jp/kaleidot725/texteditor/state/TextFieldState.kt
index b1e1c21..5d5a1b8 100644
--- a/texteditor/src/main/java/jp/kaleidot725/texteditor/state/TextFieldState.kt
+++ b/texteditor/src/main/java/jp/kaleidot725/texteditor/state/TextFieldState.kt
@@ -1,6 +1,7 @@
package jp.kaleidot725.texteditor.state
import androidx.compose.runtime.Stable
+import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.TextFieldValue
import java.util.UUID
@@ -8,5 +9,7 @@ import java.util.UUID
data class TextFieldState(
val id: String = UUID.randomUUID().toString(),
val value: TextFieldValue = TextFieldValue(),
- val isSelected: Boolean
+ val isSelected: Boolean,
+ val textStyle: TextStyle,
+ val textSelectedStyle: TextStyle
)
diff --git a/texteditor/src/main/java/jp/kaleidot725/texteditor/view/TextEditor.kt b/texteditor/src/main/java/jp/kaleidot725/texteditor/view/TextEditor.kt
index e35a920..1f9417c 100644
--- a/texteditor/src/main/java/jp/kaleidot725/texteditor/view/TextEditor.kt
+++ b/texteditor/src/main/java/jp/kaleidot725/texteditor/view/TextEditor.kt
@@ -16,6 +16,7 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
@@ -47,15 +48,16 @@ fun TextEditor(
contentPaddingValues: PaddingValues = PaddingValues(),
decorationBox: DecorationBoxComposable = { _, _, innerTextField -> innerTextField(Modifier) },
) {
- val textEditorState by rememberUpdatedState(newValue = textEditorState)
+ val textEditorStateM by rememberUpdatedState(newValue = textEditorState)
val editableController by rememberTextEditorController(
- textEditorState,
- onChanged = { onChanged(it) })
+ textEditorStateM,
+ onChanged = { onChanged(it) }
+ )
var lastScrollEvent by remember { mutableStateOf(null as ScrollEvent?) }
val lazyColumnState = rememberLazyListState()
- val focusRequesters by remember { mutableStateOf(mutableMapOf()) }
+ val focusRequesters = remember { mutableStateMapOf() }
- editableController.syncState(textEditorState)
+ editableController.syncState(textEditorStateM)
LaunchedEffect(lastScrollEvent) {
lastScrollEvent?.consume()
@@ -77,7 +79,7 @@ fun TextEditor(
contentPadding = contentPaddingValues
) {
itemsIndexed(
- items = textEditorState.fields,
+ items = textEditorStateM.fields,
key = { _, item -> item.id }
) { index, textFieldState ->
val focusRequester by remember { mutableStateOf(FocusRequester()) }
@@ -99,13 +101,13 @@ fun TextEditor(
interactionSource = remember { MutableInteractionSource() },
indication = null
) {
- if (!textEditorState.isMultipleSelectionMode) return@clickable
+ if (!textEditorStateM.isMultipleSelectionMode) return@clickable
editableController.selectField(targetIndex = index)
}
) {
TextField(
textFieldState = textFieldState,
- enabled = !textEditorState.isMultipleSelectionMode,
+ enabled = !textEditorStateM.isMultipleSelectionMode,
focusRequester = focusRequester,
onUpdateText = { newText ->
editableController.updateField(
@@ -145,9 +147,9 @@ fun TextEditor(
onDownFocus = {
if (lastScrollEvent != null && lastScrollEvent?.isConsumed != true) return@TextField
editableController.selectNextField()
- if (index != textEditorState.fields.lastIndex) lastScrollEvent =
+ if (index != textEditorStateM.fields.lastIndex) lastScrollEvent =
ScrollEvent(index + 1)
- },
+ }
)
}
}
diff --git a/texteditor/src/main/java/jp/kaleidot725/texteditor/view/TextField.kt b/texteditor/src/main/java/jp/kaleidot725/texteditor/view/TextField.kt
index a1fccc5..e2b9d73 100644
--- a/texteditor/src/main/java/jp/kaleidot725/texteditor/view/TextField.kt
+++ b/texteditor/src/main/java/jp/kaleidot725/texteditor/view/TextField.kt
@@ -8,21 +8,17 @@ import android.view.KeyEvent.KEYCODE_TAB
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.text.BasicTextField
-import androidx.compose.foundation.text.selection.LocalTextSelectionColors
-import androidx.compose.foundation.text.selection.TextSelectionColors
-import androidx.compose.material.TextFieldDefaults
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.focusTarget
import androidx.compose.ui.focus.onFocusChanged
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Color.Companion.Transparent
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.onPreviewKeyEvent
@@ -46,6 +42,12 @@ internal fun TextField(
modifier: Modifier = Modifier,
) {
val currentTextField by rememberUpdatedState(newValue = textFieldState.value)
+ val textStyle by remember(textFieldState.isSelected) {
+ derivedStateOf {
+ if (textFieldState.isSelected) textFieldState.textSelectedStyle
+ else textFieldState.textStyle
+ }
+ }
LaunchedEffect(textFieldState.isSelected) {
if (textFieldState.isSelected) {
@@ -86,7 +88,8 @@ internal fun TextField(
if (b5) return@onPreviewKeyEvent true
false
- }
+ },
+ textStyle = textStyle
)
}