Skip to content
This repository has been archived by the owner on Sep 27, 2024. It is now read-only.

Commit

Permalink
[Android] Add customisation options for links and code (#813)
Browse files Browse the repository at this point in the history
* Add ability to customise composable link color

* Allow simpler customisation of code background

* Add build config for debugging compose stability

* Use immutable collections in example app

* Enable compose compiler in core library
  • Loading branch information
jonnyandrew authored Sep 18, 2023
1 parent ac03500 commit 3e43465
Show file tree
Hide file tree
Showing 11 changed files with 208 additions and 58 deletions.
24 changes: 24 additions & 0 deletions platforms/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
alias libs.plugins.android.application apply false
Expand All @@ -16,3 +18,25 @@ def launchTask = getGradle()
if (launchTask.containsIgnoreCase("coverage")) {
apply from: 'coverage.gradle'
}

subprojects {
tasks.withType(KotlinCompile).configureEach {
compilerOptions {
if (project.findProperty("composeCompilerReports") == "true") {
freeCompilerArgs.addAll([
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" +
project.buildDir.absolutePath + "/compose_compiler"
])
}
if (project.findProperty("composeCompilerMetrics") == "true") {
freeCompilerArgs.addAll([
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" +
project.buildDir.absolutePath + "/compose_compiler"
])
}
}
}
}

1 change: 1 addition & 0 deletions platforms/android/example-compose/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,6 @@ dependencies {
implementation 'androidx.compose.ui:ui-graphics'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.material3:material3'
implementation libs.kotlin.collections.immutable
debugImplementation "androidx.compose.ui:ui-tooling"
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import io.element.android.wysiwyg.view.models.InlineFormat
import io.element.android.wysiwyg.view.models.LinkAction
import io.element.wysiwyg.compose.ui.components.FormattingButtons
import io.element.wysiwyg.compose.ui.theme.RichTextEditorTheme
import kotlinx.collections.immutable.toPersistentMap
import timber.log.Timber
import uniffi.wysiwyg_composer.ComposerAction

Expand Down Expand Up @@ -77,7 +78,7 @@ class MainActivity : ComponentActivity() {
onResetText = {
state.setHtml("")
},
actionStates = state.actions,
actionStates = state.actions.toPersistentMap(),
onActionClick = {
when (it) {
ComposerAction.BOLD -> state.toggleInlineFormat(InlineFormat.Bold)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.wysiwyg.view.models.LinkAction
import io.element.wysiwyg.compose.R
import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.persistentMapOf
import uniffi.wysiwyg_composer.ActionState
import uniffi.wysiwyg_composer.ComposerAction

@Composable
fun FormattingButtons(
actionStates: Map<ComposerAction, ActionState>,
actionStates: ImmutableMap<ComposerAction, ActionState>,
modifier: Modifier = Modifier,
onResetText: () -> Unit = {},
onActionClick: (ComposerAction) -> Unit = {},
Expand Down Expand Up @@ -214,7 +215,7 @@ private fun FormattingButton(
@Composable
private fun FormattingButtonsPreview() =
FormattingButtons(
actionStates = mapOf(
actionStates = persistentMapOf(
ComposerAction.BOLD to ActionState.ENABLED,
ComposerAction.ITALIC to ActionState.REVERSED,
ComposerAction.UNDERLINE to ActionState.DISABLED,
Expand All @@ -225,6 +226,6 @@ private fun FormattingButtonsPreview() =
@Composable
private fun FormattingButtonsDefaultPreview() =
FormattingButtons(
actionStates = emptyMap()
actionStates = persistentMapOf()
)

1 change: 1 addition & 0 deletions platforms/android/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ androidx-activity-compose = { module = "androidx.activity:activity-compose", ver
androidx-core-ktx-1_10_1 = { module = "androidx.core:core-ktx", version.ref = "core-ktx" }
kotlin-coroutines = { module="org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref="coroutines" }
kotlin-coroutines-android = { module="org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref="coroutines" }
kotlin-collections-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version = "0.3.5" }

# Android / Google
androidx-appcompat = { module="androidx.appcompat:appcompat", version.ref="appcompat" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.unit.dp
import androidx.test.espresso.Espresso.onView
Expand All @@ -24,7 +25,7 @@ class RichTextEditorStyleTest {

private val state = createState()
private val bulletRadius = MutableStateFlow(2.dp)
private val codeBg = MutableStateFlow(io.element.android.wysiwyg.R.drawable.code_block_bg)
private val codeBgColor = MutableStateFlow(Color.Blue)

@Test
fun testContentIsStillDisplayedAfterSetStyle() = runTest {
Expand All @@ -44,24 +45,25 @@ class RichTextEditorStyleTest {

@Test(expected = NotFoundException::class)
fun testBadResourceThrows() = runTest {
val nonExistentDrawable = 0
showContent()

codeBg.emit(nonExistentDrawable)
codeBgColor.emit(Color.Red)

composeTestRule.awaitIdle()
}

private fun showContent() =
composeTestRule.setContent {
val bulletRadius by bulletRadius.collectAsState()
val codeBg by codeBg.collectAsState()
val codeBgColor by codeBgColor.collectAsState()
val style = RichTextEditorDefaults.style(
bulletList = RichTextEditorDefaults.bulletListStyle(
bulletRadius = bulletRadius
),
codeBlock = RichTextEditorDefaults.codeBlockStyle(
backgroundDrawable = codeBg
background = RichTextEditorDefaults.codeBlockBackgroundStyle(
color = codeBgColor
)
)
)
MaterialTheme {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ private fun AppCompatEditText.applyStyle(style: RichTextEditorStyle) {
val cursorDrawable = ContextCompat.getDrawable(context, R.drawable.cursor)
cursorDrawable?.setTint(style.cursor.color.toArgb())
textCursorDrawable = cursorDrawable
setLinkTextColor(style.link.color.toArgb())
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package io.element.android.wysiwyg.compose

import io.element.android.wysiwyg.R as BaseR
import androidx.annotation.DrawableRes
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

private val defaultCodeCornerRadius = 4.dp
private val defaultCodeBorderWidth = 1.dp


/**
* Default config for the [RichTextEditor] composable.
*/
Expand All @@ -20,9 +23,9 @@ object RichTextEditorDefaults {
* @param codeBlock A custom style for code blocks.
* @param inlineCode A custom style for inline code.
* @param pill A custom style for pills.
* @param textColor Color of the text displayed in the editor.
* @param cursorDrawable Color of the cursor displayed in the editor.
* Only supported on API 29 and above.
* @param text A custom style for text displayed in the editor.
* @param cursor A custom style for the cursor for API 29 and above.
* @param link A custom style for links.
*/
@Composable
fun style(
Expand All @@ -32,13 +35,15 @@ object RichTextEditorDefaults {
pill: PillStyle = pillStyle(),
text: TextStyle = textStyle(),
cursor: CursorStyle = cursorStyle(),
link: LinkStyle = linkStyle(),
): RichTextEditorStyle = RichTextEditorStyle(
bulletList = bulletList,
codeBlock = codeBlock,
inlineCode = inlineCode,
pill = pill,
text = text,
cursor = cursor,
link = link,
)

/**
Expand All @@ -61,19 +66,19 @@ object RichTextEditorDefaults {
* @param leadingMargin The leading margin to apply
* @param verticalPadding The vertical padding to apply
* @param relativeTextSize The relative font scale to apply to code text
* @param backgroundDrawable The background drawable to use
* @param background The background style to apply
*/
@Composable
fun codeBlockStyle(
leadingMargin: Dp = 16.dp,
verticalPadding: Dp = 8.dp,
relativeTextSize: Float = 0.85f,
@DrawableRes
backgroundDrawable: Int = BaseR.drawable.code_block_bg,
background: CodeBackgroundStyle = codeBlockBackgroundStyle(),
) = CodeBlockStyle(
leadingMargin = leadingMargin,
verticalPadding = verticalPadding,
relativeTextSize = relativeTextSize,
backgroundDrawable = backgroundDrawable,
background = background,
)

/**
Expand All @@ -82,31 +87,19 @@ object RichTextEditorDefaults {
* @param horizontalPadding The horizontal padding to apply
* @param verticalPadding The vertical padding to apply
* @param relativeTextSize The relative font scale to apply to code text
* @param singleLineBg The background drawable to apply for single line code blocks
* @param multiLineBgLeft The background drawable to apply for the left side of multi line inline code
* @param multiLineBgMid The background drawable to apply for the middle of multi line inline code
* @param multiLineBgRight The background drawable to apply for the right side of multi line inline code
* @param background The background style to apply for single line code blocks
*/
@Composable
fun inlineCodeStyle(
horizontalPadding: Dp = 4.dp,
verticalPadding: Dp = 2.dp,
relativeTextSize: Float = 0.85f,
@DrawableRes
singleLineBg: Int = BaseR.drawable.inline_code_single_line_bg,
@DrawableRes
multiLineBgLeft: Int = BaseR.drawable.inline_code_multi_line_bg_left,
@DrawableRes
multiLineBgMid: Int = BaseR.drawable.inline_code_multi_line_bg_mid,
@DrawableRes
multiLineBgRight: Int = BaseR.drawable.inline_code_multi_line_bg_right,
background: InlineCodeBackgroundStyle = inlineCodeBackgroundStyle(),
) = InlineCodeStyle(
horizontalPadding = horizontalPadding,
verticalPadding = verticalPadding,
relativeTextSize = relativeTextSize,
singleLineBg = singleLineBg,
multiLineBgLeft = multiLineBgLeft,
multiLineBgMid = multiLineBgMid,
multiLineBgRight = multiLineBgRight,
background = background,
)

/**
Expand Down Expand Up @@ -143,4 +136,91 @@ object RichTextEditorDefaults {
) = CursorStyle(
color = color,
)

/**
* Creates the default link style for [RichTextEditor].
*
* @param color The color to apply
*/
@Composable
fun linkStyle(
color: Color = MaterialTheme.colorScheme.scrim,
) = LinkStyle(
color = color,
)

/**
* Creates a default code block background.
*/
@Composable
fun codeBlockBackgroundStyle(
color: Color = MaterialTheme.colorScheme.secondaryContainer,
borderColor: Color = MaterialTheme.colorScheme.onSecondaryContainer,
cornerRadius: Dp = defaultCodeCornerRadius,
borderWidth: Dp = defaultCodeBorderWidth,
): CodeBackgroundStyle = CodeBackgroundStyle(
density = LocalDensity.current,
color = color,
borderColor = borderColor,
borderWidth = borderWidth,
cornerRadiusTopLeft = cornerRadius,
cornerRadiusTopRight = cornerRadius,
cornerRadiusBottomRight = cornerRadius,
cornerRadiusBottomLeft = cornerRadius,
)

/**
* Creates a default inline code background.
*/
@Composable
fun inlineCodeBackgroundStyle(
color: Color = MaterialTheme.colorScheme.secondaryContainer,
borderColor: Color = MaterialTheme.colorScheme.onSecondaryContainer,
cornerRadius: Dp = defaultCodeCornerRadius,
borderWidth: Dp = defaultCodeBorderWidth
): InlineCodeBackgroundStyle {
val density = LocalDensity.current
return InlineCodeBackgroundStyle(
singleLine = CodeBackgroundStyle(
density = density,
color = color,
borderColor = borderColor,
borderWidth = borderWidth,
cornerRadiusTopLeft = cornerRadius,
cornerRadiusTopRight = cornerRadius,
cornerRadiusBottomRight = cornerRadius,
cornerRadiusBottomLeft = cornerRadius,
),
multiLineLeft = CodeBackgroundStyle(
density = density,
color = color,
borderColor = borderColor,
borderWidth = borderWidth,
cornerRadiusTopLeft = cornerRadius,
cornerRadiusTopRight = 0.dp,
cornerRadiusBottomRight = 0.dp,
cornerRadiusBottomLeft = cornerRadius,
),
multiLineRight = CodeBackgroundStyle(
density = density,
color = color,
borderColor = borderColor,
borderWidth = borderWidth,
cornerRadiusTopLeft = 0.dp,
cornerRadiusTopRight = cornerRadius,
cornerRadiusBottomRight = cornerRadius,
cornerRadiusBottomLeft = 0.dp,
),
multiLineMiddle = CodeBackgroundStyle(
density = density,
color = color,
borderColor = borderColor,
borderWidth = borderWidth,
cornerRadiusTopLeft = 0.dp,
cornerRadiusTopRight = 0.dp,
cornerRadiusBottomRight = 0.dp,
cornerRadiusBottomLeft = 0.dp,
),
)
}
}
Loading

0 comments on commit 3e43465

Please sign in to comment.