Remove padding from markdown editor and pre-render preview
This commit is contained in:
parent
c7f44e2b81
commit
7ed94aebf4
2 changed files with 126 additions and 70 deletions
|
@ -20,7 +20,6 @@ import androidx.compose.foundation.layout.size
|
|||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
|
@ -47,10 +46,7 @@ import androidx.compose.material3.Tab
|
|||
import androidx.compose.material3.TabRow
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberDrawerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
|
@ -63,17 +59,9 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
|
@ -83,7 +71,6 @@ import com.wbrawner.simplemarkdown.EditorState
|
|||
import com.wbrawner.simplemarkdown.MarkdownViewModel
|
||||
import com.wbrawner.simplemarkdown.R
|
||||
import com.wbrawner.simplemarkdown.Route
|
||||
import com.wbrawner.simplemarkdown.model.Readability
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.isActive
|
||||
|
@ -361,6 +348,7 @@ private fun TabbedMarkdownEditor(
|
|||
}
|
||||
HorizontalPager(
|
||||
modifier = Modifier.fillMaxSize(1f), state = pagerState,
|
||||
beyondBoundsPageCount = 1,
|
||||
userScrollEnabled = !lockSwiping
|
||||
) { page ->
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
@ -384,19 +372,6 @@ private fun TabbedMarkdownEditor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun String.annotate(enableReadability: Boolean): AnnotatedString {
|
||||
if (!enableReadability) return AnnotatedString(this)
|
||||
val readability = Readability(this)
|
||||
val annotated = AnnotatedString.Builder(this)
|
||||
for (sentence in readability.sentences()) {
|
||||
var color = Color.Transparent
|
||||
if (sentence.syllableCount() > 25) color = Color(229, 232, 42, 100)
|
||||
if (sentence.syllableCount() > 35) color = Color(193, 66, 66, 100)
|
||||
annotated.addStyle(SpanStyle(background = color), sentence.start(), sentence.end())
|
||||
}
|
||||
return annotated.toAnnotatedString()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MarkdownNavigationDrawer(
|
||||
navigate: (Route) -> Unit, content: @Composable (drawerState: DrawerState) -> Unit
|
||||
|
@ -482,51 +457,8 @@ fun MarkdownTopAppBar(
|
|||
IconButton(onClick = { onClick() }) {
|
||||
Icon(imageVector = icon, contentDescription = contentDescription)
|
||||
}
|
||||
}, actions = actions ?: {},
|
||||
scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MarkdownTextField(
|
||||
modifier: Modifier,
|
||||
reload: Int,
|
||||
markdown: String,
|
||||
setMarkdown: (String) -> Unit,
|
||||
enableReadability: Boolean = false
|
||||
) {
|
||||
val (selection, setSelection) = remember { mutableStateOf(TextRange.Zero) }
|
||||
val (composition, setComposition) = remember { mutableStateOf<TextRange?>(null) }
|
||||
val (textFieldValue, setTextFieldValue) = remember(reload) {
|
||||
mutableStateOf(TextFieldValue(markdown.annotate(enableReadability), selection, composition))
|
||||
}
|
||||
val setTextFieldAndViewModelValues: (TextFieldValue) -> Unit = {
|
||||
setSelection(it.selection)
|
||||
setComposition(it.composition)
|
||||
setTextFieldValue(it.copy(annotatedString = it.text.annotate(enableReadability)))
|
||||
setMarkdown(it.text)
|
||||
}
|
||||
|
||||
TextField(
|
||||
modifier = modifier.imePadding(),
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = MaterialTheme.colorScheme.surface,
|
||||
unfocusedContainerColor = MaterialTheme.colorScheme.surface,
|
||||
disabledIndicatorColor = Color.Transparent,
|
||||
errorIndicatorColor = Color.Transparent,
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent
|
||||
),
|
||||
value = textFieldValue,
|
||||
onValueChange = setTextFieldAndViewModelValues,
|
||||
placeholder = {
|
||||
Text("Markdown here…")
|
||||
},
|
||||
textStyle = TextStyle.Default.copy(
|
||||
fontFamily = FontFamily.Monospace,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
),
|
||||
keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Sentences)
|
||||
actions = actions ?: {},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
package com.wbrawner.simplemarkdown.ui
|
||||
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.wbrawner.simplemarkdown.model.Readability
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MarkdownTextField(
|
||||
modifier: Modifier = Modifier,
|
||||
markdown: String,
|
||||
setMarkdown: (String) -> Unit,
|
||||
reload: Int = 0,
|
||||
enableReadability: Boolean = false,
|
||||
) {
|
||||
val (selection, setSelection) = remember { mutableStateOf(TextRange.Zero) }
|
||||
val (composition, setComposition) = remember { mutableStateOf<TextRange?>(null) }
|
||||
val (textFieldValue, setTextFieldValue) = remember(reload) {
|
||||
mutableStateOf(TextFieldValue(markdown.annotate(enableReadability), selection, composition))
|
||||
}
|
||||
val setTextFieldAndViewModelValues: (TextFieldValue) -> Unit = {
|
||||
setSelection(it.selection)
|
||||
setComposition(it.composition)
|
||||
setTextFieldValue(it.copy(annotatedString = it.text.annotate(enableReadability)))
|
||||
setMarkdown(it.text)
|
||||
}
|
||||
val colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = MaterialTheme.colorScheme.surface,
|
||||
unfocusedContainerColor = MaterialTheme.colorScheme.surface,
|
||||
disabledIndicatorColor = Color.Transparent,
|
||||
errorIndicatorColor = Color.Transparent,
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent
|
||||
)
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.imePadding()
|
||||
.verticalScroll(
|
||||
rememberScrollState()
|
||||
)
|
||||
) {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val textStyle = TextStyle.Default.copy(
|
||||
fontFamily = FontFamily.Monospace,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
CompositionLocalProvider(LocalTextSelectionColors provides colors.textSelectionColors) {
|
||||
BasicTextField(
|
||||
value = textFieldValue,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
onValueChange = setTextFieldAndViewModelValues,
|
||||
enabled = true,
|
||||
readOnly = false,
|
||||
textStyle = textStyle,
|
||||
cursorBrush = SolidColor(colors.cursorColor),
|
||||
keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Sentences),
|
||||
keyboardActions = KeyboardActions.Default,
|
||||
interactionSource = interactionSource,
|
||||
singleLine = false,
|
||||
maxLines = Int.MAX_VALUE,
|
||||
minLines = 1,
|
||||
decorationBox = @Composable { innerTextField ->
|
||||
// places leading icon, text field with label and placeholder, trailing icon
|
||||
TextFieldDefaults.DecorationBox(
|
||||
value = textFieldValue.text,
|
||||
visualTransformation = VisualTransformation.None,
|
||||
innerTextField = innerTextField,
|
||||
placeholder = {
|
||||
Text("Markdown here…")
|
||||
},
|
||||
singleLine = false,
|
||||
enabled = true,
|
||||
interactionSource = interactionSource,
|
||||
colors = colors,
|
||||
contentPadding = PaddingValues(8.dp)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.annotate(enableReadability: Boolean): AnnotatedString {
|
||||
if (!enableReadability) return AnnotatedString(this)
|
||||
val readability = Readability(this)
|
||||
val annotated = AnnotatedString.Builder(this)
|
||||
for (sentence in readability.sentences()) {
|
||||
var color = Color.Transparent
|
||||
if (sentence.syllableCount() > 25) color = Color(229, 232, 42, 100)
|
||||
if (sentence.syllableCount() > 35) color = Color(193, 66, 66, 100)
|
||||
annotated.addStyle(SpanStyle(background = color), sentence.start(), sentence.end())
|
||||
}
|
||||
return annotated.toAnnotatedString()
|
||||
}
|
Loading…
Reference in a new issue