Add georgia font to help with heavy reading screen.
This commit is contained in:
parent
861ec795b1
commit
532f5810d7
10 changed files with 176 additions and 54 deletions
|
|
@ -60,14 +60,9 @@ interface AdventureDao {
|
|||
adventure: List<Pair<AdventureDbo, List<AdventureStoryDbo>>>
|
||||
) {
|
||||
// First clean the database from old unused data.
|
||||
booksToRemove.forEach {
|
||||
deleteBook(id = it)
|
||||
deleteStory(id = it)
|
||||
}
|
||||
storiesToRemove.forEach {
|
||||
deleteBook(id = it)
|
||||
deleteStory(id = it)
|
||||
}
|
||||
// story lines are remove with cascading foreign key.
|
||||
booksToRemove.forEach { deleteBook(id = it) }
|
||||
storiesToRemove.forEach { deleteBook(id = it) }
|
||||
// then update the data.
|
||||
adventure.forEach { (adventure, lines) ->
|
||||
// then remove the story lines before inserting them again (easier than to keep track of them all)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.pixelized.rplexicon.data.database.adventure
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
|
||||
@Entity(
|
||||
tableName = "adventures",
|
||||
|
|
@ -19,6 +20,14 @@ data class AdventureDbo(
|
|||
@Entity(
|
||||
tableName = "adventuresStories",
|
||||
primaryKeys = ["bookTitle", "adventureTitle", "index"],
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = AdventureDbo::class,
|
||||
parentColumns = ["bookTitle", "adventureTitle"],
|
||||
childColumns = ["bookTitle", "adventureTitle"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
)
|
||||
data class AdventureStoryDbo(
|
||||
val bookTitle: String,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ data class AdventureLine(
|
|||
) {
|
||||
enum class Format {
|
||||
TITLE,
|
||||
SUB_TITLE,
|
||||
CHAPTER,
|
||||
PARAGRAPH,
|
||||
DIALOGUE,
|
||||
|
|
|
|||
|
|
@ -22,19 +22,24 @@ class AdventureStoryLineParser @Inject constructor() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun ArrayList<*>.parseFormat(): AdventureLine.Format? = when (this[0] as? String) {
|
||||
TITLE -> AdventureLine.Format.TITLE
|
||||
CHAPTER -> AdventureLine.Format.CHAPTER
|
||||
PARAGRAPH -> AdventureLine.Format.PARAGRAPH
|
||||
DIALOGUE -> AdventureLine.Format.DIALOGUE
|
||||
ANNEX -> AdventureLine.Format.ANNEX
|
||||
LEGEND -> AdventureLine.Format.LEGEND
|
||||
else -> null
|
||||
}
|
||||
private fun ArrayList<*>.parseFormat(): AdventureLine.Format? =
|
||||
when (this.getOrNull(0) as? String) {
|
||||
TITLE -> AdventureLine.Format.TITLE
|
||||
SUB_TITLE -> AdventureLine.Format.SUB_TITLE
|
||||
CHAPTER -> AdventureLine.Format.CHAPTER
|
||||
PARAGRAPH -> AdventureLine.Format.PARAGRAPH
|
||||
DIALOGUE -> AdventureLine.Format.DIALOGUE
|
||||
ANNEX -> AdventureLine.Format.ANNEX
|
||||
LEGEND -> AdventureLine.Format.LEGEND
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun ArrayList<*>.parseText(): String? = this[1] as? String
|
||||
private fun ArrayList<*>.parseText(): String? =
|
||||
this.getOrNull(1) as? String
|
||||
|
||||
private fun <T> ValueRange.mapNotNull(lambda: (row: ArrayList<*>) -> T?): List<T> {
|
||||
private inline fun <T> ValueRange.mapNotNull(
|
||||
crossinline lambda: (row: ArrayList<*>) -> T?
|
||||
): List<T> {
|
||||
return values.sheet()?.mapNotNull { row: Any? ->
|
||||
when (row) {
|
||||
is ArrayList<*> -> lambda(row)
|
||||
|
|
@ -45,6 +50,7 @@ class AdventureStoryLineParser @Inject constructor() {
|
|||
|
||||
companion object {
|
||||
private const val TITLE = "Titre"
|
||||
private const val SUB_TITLE = "Sous titre"
|
||||
private const val CHAPTER = "Chapitre"
|
||||
private const val PARAGRAPH = "Paragraphe"
|
||||
private const val DIALOGUE = "Dialogue"
|
||||
|
|
|
|||
|
|
@ -23,11 +23,14 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.Saver
|
||||
import androidx.compose.runtime.saveable.mapSaver
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
|
|
@ -41,6 +44,7 @@ import androidx.compose.ui.layout.positionInParent
|
|||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -48,9 +52,12 @@ import androidx.core.net.toUri
|
|||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.ui.composable.images.BackgroundImage
|
||||
import com.pixelized.rplexicon.ui.composable.images.rememberSaturationFilter
|
||||
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
||||
import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.CHAPTER
|
||||
import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.LEGEND
|
||||
import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.PARAGRAPH
|
||||
import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.SUB_TITLE
|
||||
import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.TITLE
|
||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||
|
||||
|
|
@ -75,6 +82,12 @@ fun AdventureDetailScreen(
|
|||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
data class TitleLayoutInfo(
|
||||
val position: Float,
|
||||
val height: Float,
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun AdventureDetailContent(
|
||||
|
|
@ -92,9 +105,9 @@ private fun AdventureDetailContent(
|
|||
onBack: () -> Unit,
|
||||
) {
|
||||
val nestedScrollOffset = rememberSaveable { mutableFloatStateOf(0f) }
|
||||
val titlePosition = rememberSaveable { mutableFloatStateOf(0f) }
|
||||
val titleHeight = rememberSaveable { mutableFloatStateOf(0f) }
|
||||
|
||||
val titleLayoutInfo = rememberSaveable(stateSaver = TitleLayoutInfoSaver) {
|
||||
mutableStateOf(null)
|
||||
}
|
||||
val nestedScrollConnexion = remember {
|
||||
object : NestedScrollConnection {
|
||||
override fun onPostScroll(
|
||||
|
|
@ -109,19 +122,21 @@ private fun AdventureDetailContent(
|
|||
}
|
||||
val topAppBarAlpha = remember {
|
||||
derivedStateOf {
|
||||
when {
|
||||
-nestedScrollOffset.floatValue < titlePosition.floatValue -> 0f
|
||||
-nestedScrollOffset.floatValue > titlePosition.floatValue + titleHeight.floatValue -> 1f
|
||||
titleHeight.floatValue != 0f -> (-nestedScrollOffset.floatValue - titlePosition.floatValue) / titleHeight.floatValue
|
||||
else -> 0f
|
||||
}.coerceIn(minimumValue = 0f, maximumValue = 1f)
|
||||
titleLayoutInfo.value?.let { info ->
|
||||
when {
|
||||
-nestedScrollOffset.floatValue < info.position -> 0f
|
||||
-nestedScrollOffset.floatValue > info.position + info.height -> 1f
|
||||
info.height != 0f -> (-nestedScrollOffset.floatValue - info.position) / info.height
|
||||
else -> 0f
|
||||
}.coerceIn(minimumValue = 0f, maximumValue = 1f)
|
||||
} ?: 0f
|
||||
}
|
||||
}
|
||||
val backgroundAlpha = remember {
|
||||
derivedStateOf {
|
||||
(titlePosition.floatValue).let {
|
||||
(it + nestedScrollOffset.floatValue) / it
|
||||
}
|
||||
titleLayoutInfo.value?.let { info ->
|
||||
(info.position + nestedScrollOffset.floatValue) / info.position
|
||||
} ?: 0f
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -161,6 +176,8 @@ private fun AdventureDetailContent(
|
|||
)
|
||||
Text(
|
||||
modifier = Modifier.alpha(alpha = alpha.value),
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
text = adventureTitle.value ?: "",
|
||||
)
|
||||
},
|
||||
|
|
@ -174,15 +191,16 @@ private fun AdventureDetailContent(
|
|||
) { index, adventure ->
|
||||
val previous = adventures.value.getOrNull(index - 1)
|
||||
AdventureLine(
|
||||
modifier = when (index) {
|
||||
0 -> Modifier.onGloballyPositioned { coordinate ->
|
||||
if (titlePosition.floatValue == 0f) {
|
||||
titlePosition.floatValue = coordinate.positionInParent().y
|
||||
}
|
||||
if (titleHeight.floatValue == 0f) {
|
||||
titleHeight.floatValue = coordinate.size.height.toFloat()
|
||||
modifier = when {
|
||||
titleLayoutInfo.value == null && adventure.style == TITLE -> {
|
||||
Modifier.onGloballyPositioned { coordinate ->
|
||||
titleLayoutInfo.value = TitleLayoutInfo(
|
||||
position = coordinate.positionInParent().y,
|
||||
height = coordinate.size.height.toFloat(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> Modifier
|
||||
},
|
||||
paddingValues = rememberPaddingValues(
|
||||
|
|
@ -197,6 +215,33 @@ private fun AdventureDetailContent(
|
|||
}
|
||||
}
|
||||
|
||||
val TitleLayoutInfoSaver: Saver<TitleLayoutInfo?, Any> = run {
|
||||
val positionKey = "position"
|
||||
val heightKey = "height"
|
||||
mapSaver(
|
||||
save = {
|
||||
it?.let {
|
||||
mapOf(
|
||||
positionKey to it.position,
|
||||
heightKey to it.height,
|
||||
)
|
||||
} ?: emptyMap()
|
||||
},
|
||||
restore = {
|
||||
val position = it[positionKey] as? Float
|
||||
val height = it[heightKey] as? Float
|
||||
if (position != null && height != null) {
|
||||
TitleLayoutInfo(
|
||||
position = it[positionKey] as Float,
|
||||
height = it[heightKey] as Float,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
|
|
@ -215,10 +260,18 @@ private fun AdventureDetailPreview() {
|
|||
adventures = remember {
|
||||
mutableStateOf(
|
||||
listOf(
|
||||
AdventureUio(
|
||||
text = "Péripétie",
|
||||
style = SUB_TITLE,
|
||||
),
|
||||
AdventureUio(
|
||||
text = "La traque",
|
||||
style = TITLE,
|
||||
),
|
||||
AdventureUio(
|
||||
text = "Fake Chapter",
|
||||
style = CHAPTER,
|
||||
),
|
||||
AdventureUio(
|
||||
text = "Il était temps pour moi de partir à la chasse. Il ne restait déjà plus grand chose du loup d’il y a quelques jours. J'éteignis les dernières braises de mon feu, récupérai dagues, javelot et mon outre d’eau puis dissimulai le reste de mes biens au fond de la caverne me servant d'abri…",
|
||||
style = PARAGRAPH,
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ class AdventureDetailViewModel @Inject constructor(
|
|||
text = line.text,
|
||||
style = when (line.format) {
|
||||
Format.TITLE -> Style.TITLE
|
||||
Format.SUB_TITLE -> Style.SUB_TITLE
|
||||
Format.CHAPTER -> Style.CHAPTER
|
||||
Format.PARAGRAPH -> Style.PARAGRAPH
|
||||
Format.DIALOGUE -> Style.DIALOGUE
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@ import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.CH
|
|||
import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.DIALOGUE
|
||||
import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.LEGEND
|
||||
import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.PARAGRAPH
|
||||
import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.SUB_TITLE
|
||||
import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.TITLE
|
||||
import com.pixelized.rplexicon.utilitary.annotateMajWithDropCap
|
||||
import com.pixelized.rplexicon.utilitary.annotateWithDropCap
|
||||
import com.pixelized.rplexicon.utilitary.extentions.lexicon
|
||||
|
||||
|
|
@ -26,6 +28,7 @@ data class AdventureUio(
|
|||
) {
|
||||
enum class Style {
|
||||
TITLE,
|
||||
SUB_TITLE,
|
||||
CHAPTER,
|
||||
PARAGRAPH,
|
||||
DIALOGUE,
|
||||
|
|
@ -47,6 +50,7 @@ fun AdventureLine(
|
|||
.then(other = modifier),
|
||||
style = when (item.style) {
|
||||
TITLE -> MaterialTheme.lexicon.typography.adventure.title
|
||||
SUB_TITLE -> MaterialTheme.lexicon.typography.adventure.subTitle
|
||||
CHAPTER -> MaterialTheme.lexicon.typography.adventure.chapter
|
||||
PARAGRAPH -> MaterialTheme.lexicon.typography.adventure.paragraph
|
||||
DIALOGUE -> MaterialTheme.lexicon.typography.adventure.dialogue
|
||||
|
|
@ -54,12 +58,12 @@ fun AdventureLine(
|
|||
LEGEND -> MaterialTheme.lexicon.typography.adventure.legend
|
||||
},
|
||||
text = when (item.style) {
|
||||
TITLE -> annotateWithDropCap(
|
||||
TITLE -> annotateMajWithDropCap(
|
||||
text = item.text,
|
||||
style = MaterialTheme.lexicon.typography.adventure.dropCap.title
|
||||
)
|
||||
|
||||
CHAPTER -> annotateWithDropCap(
|
||||
CHAPTER -> annotateMajWithDropCap(
|
||||
text = item.text,
|
||||
style = MaterialTheme.lexicon.typography.adventure.dropCap.chapter
|
||||
)
|
||||
|
|
@ -81,13 +85,22 @@ fun rememberPaddingValues(
|
|||
): PaddingValues {
|
||||
return PaddingValues(
|
||||
top = when (current.style) {
|
||||
TITLE -> 32.dp
|
||||
TITLE -> when (previous?.style) {
|
||||
TITLE -> 0.dp
|
||||
SUB_TITLE -> 0.dp
|
||||
else -> 32.dp
|
||||
}
|
||||
|
||||
CHAPTER -> 32.dp
|
||||
SUB_TITLE -> 0.dp
|
||||
|
||||
CHAPTER -> when (previous?.style) {
|
||||
TITLE -> 64.dp
|
||||
else -> 32.dp
|
||||
}
|
||||
|
||||
PARAGRAPH -> when (previous?.style) {
|
||||
PARAGRAPH -> 8.dp
|
||||
TITLE -> 32.dp
|
||||
TITLE -> 64.dp
|
||||
else -> 16.dp
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +109,10 @@ fun rememberPaddingValues(
|
|||
else -> 16.dp
|
||||
}
|
||||
|
||||
ANNEX -> 16.dp
|
||||
ANNEX -> when (previous?.style) {
|
||||
ANNEX -> 8.dp
|
||||
else -> 16.dp
|
||||
}
|
||||
|
||||
LEGEND -> when (previous?.style) {
|
||||
LEGEND -> 8.dp
|
||||
|
|
|
|||
|
|
@ -25,6 +25,11 @@ val stampFontFamily = FontFamily(
|
|||
Font(resId = R.font.rubber_stamp, weight = FontWeight.Normal),
|
||||
)
|
||||
|
||||
@Stable
|
||||
val georgia = FontFamily(
|
||||
Font(resId = R.font.georgia),
|
||||
)
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
@Stable
|
||||
data class LexiconTypography(
|
||||
|
|
@ -55,6 +60,7 @@ data class LexiconTypography(
|
|||
data class Adventure(
|
||||
val dropCap: DropCap,
|
||||
val title: TextStyle,
|
||||
val subTitle: TextStyle,
|
||||
val chapter: TextStyle,
|
||||
val paragraph: TextStyle,
|
||||
val dialogue: TextStyle,
|
||||
|
|
@ -204,25 +210,46 @@ fun lexiconTypography(
|
|||
).toSpanStyle(),
|
||||
),
|
||||
adventure: LexiconTypography.Adventure = LexiconTypography.Adventure(
|
||||
title = base.displaySmall.copy(
|
||||
title = base.displayMedium.copy(
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
),
|
||||
subTitle = base.bodyMedium.copy(
|
||||
fontFamily = georgia,
|
||||
fontSize = base.bodyMedium.fontSize.times(other = 1.1f),
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.Normal,
|
||||
),
|
||||
chapter = base.titleLarge.copy(
|
||||
textAlign = TextAlign.Start,
|
||||
fontWeight = FontWeight.Normal,
|
||||
),
|
||||
chapter = base.titleLarge,
|
||||
paragraph = base.bodyMedium.copy(
|
||||
fontFamily = georgia,
|
||||
fontSize = base.bodyMedium.fontSize.times(other = 1.1f),
|
||||
textAlign = TextAlign.Justify,
|
||||
fontWeight = FontWeight.Normal,
|
||||
),
|
||||
dialogue = base.bodyMedium,
|
||||
legend = base.labelMedium.copy(
|
||||
fontWeight = FontWeight.Light,
|
||||
dialogue = base.bodyMedium.copy(
|
||||
fontFamily = georgia,
|
||||
fontSize = base.bodyMedium.fontSize.times(other = 1.1f),
|
||||
textAlign = TextAlign.Start,
|
||||
fontWeight = FontWeight.Normal,
|
||||
),
|
||||
legend = base.labelSmall.copy(
|
||||
fontFamily = georgia,
|
||||
textAlign = TextAlign.End,
|
||||
fontWeight = FontWeight.Light,
|
||||
fontStyle = FontStyle.Italic,
|
||||
),
|
||||
annex = base.labelMedium.copy(
|
||||
fontFamily = georgia,
|
||||
textAlign = TextAlign.Start,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontStyle = FontStyle.Italic,
|
||||
),
|
||||
dropCap = LexiconTypography.Adventure.DropCap(
|
||||
title = dropCap.displaySmall,
|
||||
title = dropCap.displayMedium,
|
||||
chapter = dropCap.titleLarge,
|
||||
paragraph = dropCap.bodyMedium,
|
||||
)
|
||||
|
|
|
|||
BIN
app/src/main/res/font/georgia.ttf
Normal file
BIN
app/src/main/res/font/georgia.ttf
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue