Rework the UI of the character sheet to fit the paper one.

This commit is contained in:
Thomas Andres Gomez 2023-09-18 15:55:22 +02:00
parent f840777929
commit 5fe6cd0c01
16 changed files with 1024 additions and 1100 deletions

View file

@ -5,14 +5,12 @@ import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.ui.screens.character.CharacterSheetUio
import com.pixelized.rplexicon.ui.screens.character.composable.LabelPointUio
import com.pixelized.rplexicon.ui.screens.character.composable.ProficiencyUio
import com.pixelized.rplexicon.ui.screens.character.composable.SavingsThrowsUio
import com.pixelized.rplexicon.ui.screens.character.composable.StatUio
import com.pixelized.rplexicon.utilitary.extentions.modifier
import com.pixelized.rplexicon.utilitary.extentions.toLabel
import javax.inject.Inject
class ConvertCharacterSheetIntoDisplayableFactory @Inject constructor() {
fun toUio(sheet: CharacterSheet): CharacterSheetUio {
return CharacterSheetUio(
armorClass = LabelPointUio(
@ -20,183 +18,176 @@ class ConvertCharacterSheetIntoDisplayableFactory @Inject constructor() {
value = "${sheet.armorClass}",
max = null,
),
speed = LabelPointUio(
label = R.string.character_sheet_title_speed,
value = "${sheet.speed}",
max = "m",
),
hitPoint = LabelPointUio(
label = R.string.character_sheet_title_hp,
value = "${sheet.hitPoint}",
max = "${sheet.maxHitPoint}",
max = "/ ${sheet.maxHitPoint}",
),
initiative = LabelPointUio(
label = R.string.character_sheet_title_initiative,
value = sheet.dexterity.modifier.toLabel(),
max = null,
),
strength = StatUio(
label = R.string.character_sheet_stat_strength,
value = sheet.strength,
modifier = sheet.strength.modifier,
stats = listOf(
StatUio(
id = StatUio.ID.STRENGTH,
value = sheet.strength,
modifier = sheet.strength.modifier,
),
StatUio(
id = StatUio.ID.DEXTERITY,
value = sheet.dexterity,
modifier = sheet.dexterity.modifier,
),
StatUio(
id = StatUio.ID.CONSTITUTION,
value = sheet.constitution,
modifier = sheet.constitution.modifier,
),
StatUio(
id = StatUio.ID.INTELLIGENCE,
value = sheet.intelligence,
modifier = sheet.intelligence.modifier,
),
StatUio(
id = StatUio.ID.WISDOM,
value = sheet.wisdom,
modifier = sheet.wisdom.modifier,
),
StatUio(
id = StatUio.ID.CHARISMA,
value = sheet.charisma,
modifier = sheet.charisma.modifier,
),
),
dexterity = StatUio(
label = R.string.character_sheet_stat_dexterity,
value = sheet.dexterity,
modifier = sheet.dexterity.modifier,
savingThrows = listOf(
ProficiencyUio(
id = ProficiencyUio.ID.STRENGTH_SAVING_THROW,
multiplier = sheet.strengthSavingThrows,
modifier = sheet.strength.modifier + sheet.strengthSavingThrows * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.DEXTERITY_SAVING_THROW,
multiplier = sheet.dexteritySavingThrows,
modifier = sheet.dexterity.modifier + sheet.dexteritySavingThrows * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.CONSTITUTION_SAVING_THROW,
multiplier = sheet.constitutionSavingThrows,
modifier = sheet.constitution.modifier + sheet.constitutionSavingThrows * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.INTELLIGENCE_SAVING_THROW,
multiplier = sheet.intelligenceSavingThrows,
modifier = sheet.intelligence.modifier + sheet.intelligenceSavingThrows * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.WISDOM_SAVING_THROW,
multiplier = sheet.wisdomSavingThrows,
modifier = sheet.wisdom.modifier + sheet.wisdomSavingThrows * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.CHARISMA_SAVING_THROW,
multiplier = sheet.charismaSavingThrows,
modifier = sheet.charisma.modifier + sheet.charismaSavingThrows * sheet.proficiency,
),
),
constitution = StatUio(
label = R.string.character_sheet_stat_constitution,
value = sheet.constitution,
modifier = sheet.constitution.modifier,
),
intelligence = StatUio(
label = R.string.character_sheet_stat_intelligence,
value = sheet.intelligence,
modifier = sheet.intelligence.modifier,
),
wisdom = StatUio(
label = R.string.character_sheet_stat_wisdom,
value = sheet.wisdom,
modifier = sheet.wisdom.modifier,
),
charisma = StatUio(
label = R.string.character_sheet_stat_charisma,
value = sheet.charisma,
modifier = sheet.charisma.modifier,
),
strengthSavingThrows = SavingsThrowsUio(
label = R.string.character_sheet_stat_strength,
multiplier = sheet.strengthSavingThrows,
modifier = sheet.strength.modifier + sheet.strengthSavingThrows * sheet.proficiency,
),
dexteritySavingThrows = SavingsThrowsUio(
label = R.string.character_sheet_stat_dexterity,
multiplier = sheet.dexteritySavingThrows,
modifier = sheet.dexterity.modifier + sheet.dexteritySavingThrows * sheet.proficiency,
),
constitutionSavingThrows = SavingsThrowsUio(
label = R.string.character_sheet_stat_constitution,
multiplier = sheet.constitutionSavingThrows,
modifier = sheet.constitution.modifier + sheet.constitutionSavingThrows * sheet.proficiency,
),
intelligenceSavingThrows = SavingsThrowsUio(
label = R.string.character_sheet_stat_intelligence,
multiplier = sheet.intelligenceSavingThrows,
modifier = sheet.intelligence.modifier + sheet.intelligenceSavingThrows * sheet.proficiency,
),
wisdomSavingThrows = SavingsThrowsUio(
label = R.string.character_sheet_stat_wisdom,
multiplier = sheet.wisdomSavingThrows,
modifier = sheet.wisdom.modifier + sheet.wisdomSavingThrows * sheet.proficiency,
),
charismaSavingThrows = SavingsThrowsUio(
label = R.string.character_sheet_stat_charisma,
multiplier = sheet.charismaSavingThrows,
modifier = sheet.charisma.modifier + sheet.charismaSavingThrows * sheet.proficiency,
),
acrobatics = ProficiencyUio(
label = R.string.character_sheet_proficiency_acrobatics,
related = R.string.character_sheet_stat_dexterity_short,
multiplier = sheet.acrobatics,
modifier = sheet.dexterity.modifier + sheet.acrobatics * sheet.proficiency,
),
animalHandling = ProficiencyUio(
label = R.string.character_sheet_proficiency_animal_handling,
related = R.string.character_sheet_stat_wisdom_short,
multiplier = sheet.animalHandling,
modifier = sheet.wisdom.modifier + sheet.animalHandling * sheet.proficiency,
),
arcana = ProficiencyUio(
label = R.string.character_sheet_proficiency_arcana,
related = R.string.character_sheet_stat_intelligence_short,
multiplier = sheet.arcana,
modifier = sheet.intelligence.modifier + sheet.arcana * sheet.proficiency,
),
athletics = ProficiencyUio(
label = R.string.character_sheet_proficiency_athletics,
related = R.string.character_sheet_stat_strength_short,
multiplier = sheet.athletics,
modifier = sheet.strength.modifier + sheet.athletics * sheet.proficiency,
),
deception = ProficiencyUio(
label = R.string.character_sheet_proficiency_deception,
related = R.string.character_sheet_stat_charisma_short,
multiplier = sheet.deception,
modifier = sheet.charisma.modifier + sheet.deception * sheet.proficiency,
),
history = ProficiencyUio(
label = R.string.character_sheet_proficiency_history,
related = R.string.character_sheet_stat_intelligence_short,
multiplier = sheet.history,
modifier = sheet.intelligence.modifier + sheet.history * sheet.proficiency,
),
insight = ProficiencyUio(
label = R.string.character_sheet_proficiency_insight,
related = R.string.character_sheet_stat_wisdom_short,
multiplier = sheet.insight,
modifier = sheet.wisdom.modifier + sheet.insight * sheet.proficiency,
),
intimidation = ProficiencyUio(
label = R.string.character_sheet_proficiency_intimidation,
related = R.string.character_sheet_stat_charisma_short,
multiplier = sheet.intimidation,
modifier = sheet.charisma.modifier + sheet.intimidation * sheet.proficiency,
),
investigation = ProficiencyUio(
label = R.string.character_sheet_proficiency_investigation,
related = R.string.character_sheet_stat_intelligence_short,
multiplier = sheet.investigation,
modifier = sheet.intelligence.modifier + sheet.investigation * sheet.proficiency,
),
medicine = ProficiencyUio(
label = R.string.character_sheet_proficiency_medicine,
related = R.string.character_sheet_stat_wisdom_short,
multiplier = sheet.medicine,
modifier = sheet.wisdom.modifier + sheet.medicine * sheet.proficiency,
),
nature = ProficiencyUio(
label = R.string.character_sheet_proficiency_nature,
related = R.string.character_sheet_stat_intelligence_short,
multiplier = sheet.nature,
modifier = sheet.intelligence.modifier + sheet.nature * sheet.proficiency,
),
perception = ProficiencyUio(
label = R.string.character_sheet_proficiency_perception,
related = R.string.character_sheet_stat_wisdom_short,
multiplier = sheet.perception,
modifier = sheet.wisdom.modifier + sheet.perception * sheet.proficiency,
),
performance = ProficiencyUio(
label = R.string.character_sheet_proficiency_performance,
related = R.string.character_sheet_stat_charisma_short,
multiplier = sheet.performance,
modifier = sheet.charisma.modifier + sheet.performance * sheet.proficiency,
),
persuasion = ProficiencyUio(
label = R.string.character_sheet_proficiency_persuasion,
related = R.string.character_sheet_stat_charisma_short,
multiplier = sheet.persuasion,
modifier = sheet.charisma.modifier + sheet.persuasion * sheet.proficiency,
),
religion = ProficiencyUio(
label = R.string.character_sheet_proficiency_religion,
related = R.string.character_sheet_stat_intelligence_short,
multiplier = sheet.religion,
modifier = sheet.intelligence.modifier + sheet.religion * sheet.proficiency,
),
sleightOfHand = ProficiencyUio(
label = R.string.character_sheet_proficiency_sleight_of_hand,
related = R.string.character_sheet_stat_dexterity_short,
multiplier = sheet.sleightOfHand,
modifier = sheet.dexterity.modifier + sheet.sleightOfHand * sheet.proficiency,
),
stealth = ProficiencyUio(
label = R.string.character_sheet_proficiency_stealth,
related = R.string.character_sheet_stat_dexterity_short,
multiplier = sheet.stealth,
modifier = sheet.dexterity.modifier + sheet.stealth * sheet.proficiency,
),
survival = ProficiencyUio(
label = R.string.character_sheet_proficiency_survival,
related = R.string.character_sheet_stat_wisdom_short,
multiplier = sheet.survival,
modifier = sheet.wisdom.modifier + sheet.survival * sheet.proficiency,
proficiencies = listOf(
ProficiencyUio(
id = ProficiencyUio.ID.ACROBATICS,
multiplier = sheet.acrobatics,
modifier = sheet.dexterity.modifier + sheet.acrobatics * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.ARCANA,
multiplier = sheet.arcana,
modifier = sheet.intelligence.modifier + sheet.arcana * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.ATHLETICS,
multiplier = sheet.athletics,
modifier = sheet.strength.modifier + sheet.athletics * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.STEALTH,
multiplier = sheet.stealth,
modifier = sheet.dexterity.modifier + sheet.stealth * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.ANIMAL_HANDLING,
multiplier = sheet.animalHandling,
modifier = sheet.wisdom.modifier + sheet.animalHandling * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.SLEIGHT_OF_HAND,
multiplier = sheet.sleightOfHand,
modifier = sheet.dexterity.modifier + sheet.sleightOfHand * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.HISTORY,
multiplier = sheet.history,
modifier = sheet.intelligence.modifier + sheet.history * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.INTIMIDATION,
multiplier = sheet.intimidation,
modifier = sheet.charisma.modifier + sheet.intimidation * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.INSIGHT,
multiplier = sheet.insight,
modifier = sheet.wisdom.modifier + sheet.insight * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.INVESTIGATION,
multiplier = sheet.investigation,
modifier = sheet.intelligence.modifier + sheet.investigation * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.MEDICINE,
multiplier = sheet.medicine,
modifier = sheet.wisdom.modifier + sheet.medicine * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.NATURE,
multiplier = sheet.nature,
modifier = sheet.intelligence.modifier + sheet.nature * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.PERCEPTION,
multiplier = sheet.perception,
modifier = sheet.wisdom.modifier + sheet.perception * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.PERSUASION,
multiplier = sheet.persuasion,
modifier = sheet.charisma.modifier + sheet.persuasion * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.RELIGION,
multiplier = sheet.religion,
modifier = sheet.intelligence.modifier + sheet.religion * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.PERFORMANCE,
multiplier = sheet.performance,
modifier = sheet.charisma.modifier + sheet.performance * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.SURVIVAL,
multiplier = sheet.survival,
modifier = sheet.wisdom.modifier + sheet.survival * sheet.proficiency,
),
ProficiencyUio(
id = ProficiencyUio.ID.DECEPTION,
multiplier = sheet.deception,
modifier = sheet.charisma.modifier + sheet.deception * sheet.proficiency,
),
),
)
}

View file

@ -22,9 +22,12 @@ class CharacterSheetParser @Inject constructor() {
// prepare a hash on the structure & function to get index in it.
lateinit var structure: Map<String, Int>
fun List<*>.parse(key: String): Int =
fun List<*>.parseInt(key: String): Int =
(getOrNull(structure.getValue(key)) as? String)?.toIntOrNull() ?: 0
fun List<*>.parseString(key: String): String? =
(getOrNull(structure.getValue(key)) as? String)
// parse the sheet
return sheet?.mapIndexedNotNull { index, item ->
when (index) {
@ -34,44 +37,45 @@ class CharacterSheetParser @Inject constructor() {
}
else -> {
val name = item.getOrNull(structure.getValue(NAME)) as String?
val name = item.parseString(NAME)
if (name != null) {
CharacterSheet(
name = name,
hitPoint = item.parse(HIT_POINT),
maxHitPoint = item.parse(MAX_HIT_POINT),
armorClass = item.parse(ARMOR_CLASS),
proficiency = item.parse(MASTERY),
strength = item.parse(STRENGTH),
dexterity = item.parse(DEXTERITY),
constitution = item.parse(CONSTITUTION),
intelligence = item.parse(INTELLIGENCE),
wisdom = item.parse(WISDOM),
charisma = item.parse(CHARISMA),
strengthSavingThrows = item.parse(STRENGTH_SAVING_THROW),
dexteritySavingThrows = item.parse(DEXTERITY_SAVING_THROW),
constitutionSavingThrows = item.parse(CONSTITUTION_SAVING_THROW),
intelligenceSavingThrows = item.parse(INTELLIGENCE_SAVING_THROW),
wisdomSavingThrows = item.parse(WISDOM_SAVING_THROW),
charismaSavingThrows = item.parse(CHARISMA_SAVING_THROW),
acrobatics = item.parse(ACROBATICS),
animalHandling = item.parse(ANIMAL_HANDLING),
arcana = item.parse(ARCANA),
athletics = item.parse(ATHLETICS),
deception = item.parse(DECEPTION),
history = item.parse(HISTORY),
insight = item.parse(INSIGHT),
intimidation = item.parse(INTIMIDATION),
investigation = item.parse(INVESTIGATION),
medicine = item.parse(MEDICINE),
nature = item.parse(NATURE),
perception = item.parse(PERCEPTION),
performance = item.parse(PERFORMANCE),
persuasion = item.parse(PERSUASION),
religion = item.parse(RELIGION),
sleightOfHand = item.parse(SLEIGHT_OF_HAND),
stealth = item.parse(STEALTH),
survival = item.parse(SURVIVAL),
hitPoint = item.parseString(HIT_POINT) ?: "1",
maxHitPoint = item.parseString(MAX_HIT_POINT) ?: "1",
armorClass = item.parseString(ARMOR_CLASS) ?: "0",
speed = item.parseInt(SPEED),
proficiency = item.parseInt(MASTERY),
strength = item.parseInt(STRENGTH),
dexterity = item.parseInt(DEXTERITY),
constitution = item.parseInt(CONSTITUTION),
intelligence = item.parseInt(INTELLIGENCE),
wisdom = item.parseInt(WISDOM),
charisma = item.parseInt(CHARISMA),
strengthSavingThrows = item.parseInt(STRENGTH_SAVING_THROW),
dexteritySavingThrows = item.parseInt(DEXTERITY_SAVING_THROW),
constitutionSavingThrows = item.parseInt(CONSTITUTION_SAVING_THROW),
intelligenceSavingThrows = item.parseInt(INTELLIGENCE_SAVING_THROW),
wisdomSavingThrows = item.parseInt(WISDOM_SAVING_THROW),
charismaSavingThrows = item.parseInt(CHARISMA_SAVING_THROW),
acrobatics = item.parseInt(ACROBATICS),
animalHandling = item.parseInt(ANIMAL_HANDLING),
arcana = item.parseInt(ARCANA),
athletics = item.parseInt(ATHLETICS),
deception = item.parseInt(DECEPTION),
history = item.parseInt(HISTORY),
insight = item.parseInt(INSIGHT),
intimidation = item.parseInt(INTIMIDATION),
investigation = item.parseInt(INVESTIGATION),
medicine = item.parseInt(MEDICINE),
nature = item.parseInt(NATURE),
perception = item.parseInt(PERCEPTION),
performance = item.parseInt(PERFORMANCE),
persuasion = item.parseInt(PERSUASION),
religion = item.parseInt(RELIGION),
sleightOfHand = item.parseInt(SLEIGHT_OF_HAND),
stealth = item.parseInt(STEALTH),
survival = item.parseInt(SURVIVAL),
)
} else {
null
@ -86,6 +90,7 @@ class CharacterSheetParser @Inject constructor() {
private const val HIT_POINT = "Point de vie"
private const val MAX_HIT_POINT = "Point de vie MAX"
private const val ARMOR_CLASS = "Classe d'armure"
private const val SPEED = "Vitesse"
private const val MASTERY = "Bonus de maîtrise"
private const val STRENGTH = "Force"
private const val DEXTERITY = "Dextérité"
@ -124,6 +129,7 @@ class CharacterSheetParser @Inject constructor() {
HIT_POINT,
MAX_HIT_POINT,
ARMOR_CLASS,
SPEED,
MASTERY,
STRENGTH,
DEXTERITY,

View file

@ -2,9 +2,10 @@ package com.pixelized.rplexicon.model
data class CharacterSheet(
val name: String,
val hitPoint: Int, // Point de vie
val maxHitPoint: Int,
val armorClass: Int, // Classe d'armure
val hitPoint: String, // Point de vie
val maxHitPoint: String,
val armorClass: String, // Classe d'armure
val speed: Int,
val proficiency: Int, // Bonus de maîtrise
val strength: Int, // Force
val dexterity: Int, // Dextérité

View file

@ -85,7 +85,7 @@ fun BlurredOverlayHost(
Box(
modifier = Modifier
.clickableInterceptor()
.background(brush = MaterialTheme.lexicon.colorScheme.rollOverlayBrush)
.background(color = MaterialTheme.lexicon.colorScheme.rollOverlayBrush)
.fillMaxSize()
.systemBarsPadding(),
) {

View file

@ -2,7 +2,6 @@ package com.pixelized.rplexicon.ui.screens.character
import android.content.res.Configuration
import androidx.activity.compose.BackHandler
import androidx.annotation.StringRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Arrangement
@ -61,11 +60,7 @@ import com.pixelized.rplexicon.ui.screens.character.composable.Counter
import com.pixelized.rplexicon.ui.screens.character.composable.CounterUio
import com.pixelized.rplexicon.ui.screens.character.composable.LabelPoint
import com.pixelized.rplexicon.ui.screens.character.composable.LabelPointUio
import com.pixelized.rplexicon.ui.screens.character.composable.Proficiency
import com.pixelized.rplexicon.ui.screens.character.composable.ProficiencyUio
import com.pixelized.rplexicon.ui.screens.character.composable.SavingsThrows
import com.pixelized.rplexicon.ui.screens.character.composable.SavingsThrowsUio
import com.pixelized.rplexicon.ui.screens.character.composable.Stat
import com.pixelized.rplexicon.ui.screens.character.composable.StatUio
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.ddBorder
@ -74,38 +69,12 @@ import kotlinx.coroutines.launch
@Stable
data class CharacterSheetUio(
val armorClass: LabelPointUio,
val speed: LabelPointUio,
val hitPoint: LabelPointUio,
val initiative: LabelPointUio,
val strength: StatUio,
val dexterity: StatUio,
val constitution: StatUio,
val intelligence: StatUio,
val wisdom: StatUio,
val charisma: StatUio,
val strengthSavingThrows: SavingsThrowsUio,
val dexteritySavingThrows: SavingsThrowsUio,
val constitutionSavingThrows: SavingsThrowsUio,
val intelligenceSavingThrows: SavingsThrowsUio,
val wisdomSavingThrows: SavingsThrowsUio,
val charismaSavingThrows: SavingsThrowsUio,
val acrobatics: ProficiencyUio,
val animalHandling: ProficiencyUio,
val arcana: ProficiencyUio,
val athletics: ProficiencyUio,
val deception: ProficiencyUio,
val history: ProficiencyUio,
val insight: ProficiencyUio,
val intimidation: ProficiencyUio,
val investigation: ProficiencyUio,
val medicine: ProficiencyUio,
val nature: ProficiencyUio,
val perception: ProficiencyUio,
val performance: ProficiencyUio,
val persuasion: ProficiencyUio,
val religion: ProficiencyUio,
val sleightOfHand: ProficiencyUio,
val stealth: ProficiencyUio,
val survival: ProficiencyUio,
val stats: List<StatUio>,
val savingThrows: List<ProficiencyUio>,
val proficiencies: List<ProficiencyUio>,
)
@OptIn(ExperimentalMaterialApi::class)
@ -146,153 +115,45 @@ fun CharacterSheetScreen(
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onStrength = {
val roll = viewModel.strengthRoll()
onStats = { stat ->
val roll = when (stat.id) {
StatUio.ID.STRENGTH -> viewModel.strengthRoll()
StatUio.ID.DEXTERITY -> viewModel.dexterityRoll()
StatUio.ID.CONSTITUTION -> viewModel.constitutionRoll()
StatUio.ID.INTELLIGENCE -> viewModel.intelligenceRoll()
StatUio.ID.WISDOM -> viewModel.wisdomRoll()
StatUio.ID.CHARISMA -> viewModel.charismaRoll()
}
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onDexterity = {
val roll = viewModel.dexterityRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onConstitution = {
val roll = viewModel.constitutionRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onIntelligence = {
val roll = viewModel.intelligenceRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onWisdom = {
val roll = viewModel.wisdomRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onCharisma = {
val roll = viewModel.charismaRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onStrengthSavingThrows = {
val roll = viewModel.strengthSavingThrowsRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onDexteritySavingThrows = {
val roll = viewModel.dexteritySavingThrowsRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onConstitutionSavingThrows = {
val roll = viewModel.constitutionSavingThrowsRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onIntelligenceSavingThrows = {
val roll = viewModel.intelligenceSavingThrowsRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onWisdomSavingThrows = {
val roll = viewModel.wisdomSavingThrowsRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onCharismaSavingThrows = {
val roll = viewModel.charismaSavingThrowsRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onAcrobatics = {
val roll = viewModel.acrobaticsRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onAnimalHandling = {
val roll = viewModel.animalHandlingRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onArcana = {
val roll = viewModel.arcanaRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onAthletics = {
val roll = viewModel.athleticsRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onDeception = {
val roll = viewModel.deceptionRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onHistory = {
val roll = viewModel.historyRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onInsight = {
val roll = viewModel.insightRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onIntimidation = {
val roll = viewModel.intimidationRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onInvestigation = {
val roll = viewModel.investigationRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onMedicine = {
val roll = viewModel.medicineRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onNature = {
val roll = viewModel.natureRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onPerception = {
val roll = viewModel.perceptionRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onPerformance = {
val roll = viewModel.performanceRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onPersuasion = {
val roll = viewModel.persuasionRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onReligion = {
val roll = viewModel.religionRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onSleightOfHand = {
val roll = viewModel.sleightOfHandRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onStealth = {
val roll = viewModel.stealthRoll()
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
onSurvival = {
val roll = viewModel.survivalRoll()
onProficiencies = { proficiency ->
val roll = when (proficiency.id) {
ProficiencyUio.ID.STRENGTH_SAVING_THROW -> viewModel.strengthRoll()
ProficiencyUio.ID.DEXTERITY_SAVING_THROW -> viewModel.dexterityRoll()
ProficiencyUio.ID.CONSTITUTION_SAVING_THROW -> viewModel.constitutionRoll()
ProficiencyUio.ID.INTELLIGENCE_SAVING_THROW -> viewModel.intelligenceRoll()
ProficiencyUio.ID.WISDOM_SAVING_THROW -> viewModel.wisdomRoll()
ProficiencyUio.ID.CHARISMA_SAVING_THROW -> viewModel.charismaRoll()
ProficiencyUio.ID.ACROBATICS -> viewModel.acrobaticsRoll()
ProficiencyUio.ID.ANIMAL_HANDLING -> viewModel.animalHandlingRoll()
ProficiencyUio.ID.ARCANA -> viewModel.arcanaRoll()
ProficiencyUio.ID.ATHLETICS -> viewModel.athleticsRoll()
ProficiencyUio.ID.DECEPTION -> viewModel.deceptionRoll()
ProficiencyUio.ID.HISTORY -> viewModel.historyRoll()
ProficiencyUio.ID.INSIGHT -> viewModel.insightRoll()
ProficiencyUio.ID.INTIMIDATION -> viewModel.intimidationRoll()
ProficiencyUio.ID.INVESTIGATION -> viewModel.investigationRoll()
ProficiencyUio.ID.MEDICINE -> viewModel.medicineRoll()
ProficiencyUio.ID.NATURE -> viewModel.natureRoll()
ProficiencyUio.ID.PERCEPTION -> viewModel.perceptionRoll()
ProficiencyUio.ID.PERFORMANCE -> viewModel.performanceRoll()
ProficiencyUio.ID.PERSUASION -> viewModel.persuasionRoll()
ProficiencyUio.ID.RELIGION -> viewModel.religionRoll()
ProficiencyUio.ID.SLEIGHT_OF_HAND -> viewModel.sleightOfHandRoll()
ProficiencyUio.ID.STEALTH -> viewModel.stealthRoll()
ProficiencyUio.ID.SURVIVAL -> viewModel.survivalRoll()
}
overlay.prepareRoll(roll = roll)
overlay.showOverlay()
},
@ -328,37 +189,9 @@ private fun CharacterSheetContent(
counter: State<List<CounterUio>>,
alterations: State<List<String>>,
onBack: () -> Unit,
onStats: (StatUio) -> Unit,
onProficiencies: (ProficiencyUio) -> Unit,
onInitiative: () -> Unit,
onStrength: () -> Unit,
onDexterity: () -> Unit,
onConstitution: () -> Unit,
onIntelligence: () -> Unit,
onWisdom: () -> Unit,
onCharisma: () -> Unit,
onStrengthSavingThrows: () -> Unit,
onDexteritySavingThrows: () -> Unit,
onConstitutionSavingThrows: () -> Unit,
onIntelligenceSavingThrows: () -> Unit,
onWisdomSavingThrows: () -> Unit,
onCharismaSavingThrows: () -> Unit,
onAcrobatics: () -> Unit,
onAnimalHandling: () -> Unit,
onArcana: () -> Unit,
onAthletics: () -> Unit,
onDeception: () -> Unit,
onHistory: () -> Unit,
onInsight: () -> Unit,
onIntimidation: () -> Unit,
onInvestigation: () -> Unit,
onMedicine: () -> Unit,
onNature: () -> Unit,
onPerception: () -> Unit,
onPerformance: () -> Unit,
onPersuasion: () -> Unit,
onReligion: () -> Unit,
onSleightOfHand: () -> Unit,
onStealth: () -> Unit,
onSurvival: () -> Unit,
onHit: (id: String) -> Unit,
onDamage: (id: String) -> Unit,
) {
@ -407,200 +240,24 @@ private fun CharacterSheetContent(
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.spacedBy(
16.dp,
Alignment.CenterHorizontally
space = 16.dp,
alignment = Alignment.CenterHorizontally
),
) {
LabelPoint(
style = MaterialTheme.typography.displaySmall,
hp = sheet.armorClass,
)
LabelPoint(
hp = sheet.hitPoint,
)
LabelPoint(
style = MaterialTheme.typography.displaySmall,
hp = sheet.initiative,
onClick = onInitiative,
)
LabelPoint(label = sheet.armorClass)
LabelPoint(label = sheet.hitPoint)
LabelPoint(label = sheet.speed)
}
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
Row(
modifier = Modifier.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
) {
Stat(
modifier = Modifier.weight(1f),
stat = sheet.strength,
onClick = onStrength,
)
Stat(
modifier = Modifier.weight(1f),
stat = sheet.dexterity,
onClick = onDexterity,
)
Stat(
modifier = Modifier.weight(1f),
stat = sheet.constitution,
onClick = onConstitution,
)
}
Row(
modifier = Modifier.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
) {
Stat(
modifier = Modifier.weight(1f),
stat = sheet.intelligence,
onClick = onIntelligence,
)
Stat(
modifier = Modifier.weight(1f),
stat = sheet.wisdom,
onClick = onWisdom,
)
Stat(
modifier = Modifier.weight(1f),
stat = sheet.charisma,
onClick = onCharisma,
)
}
}
SectionTitle(
modifier = Modifier.padding(top = 16.dp),
title = R.string.character_sheet_title_saving_throws,
)
Row(
modifier = Modifier.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
SavingsThrows(
savingsThrows = sheet.strengthSavingThrows,
onClick = onStrengthSavingThrows,
)
SavingsThrows(
savingsThrows = sheet.dexteritySavingThrows,
onClick = onDexteritySavingThrows,
)
SavingsThrows(
savingsThrows = sheet.constitutionSavingThrows,
onClick = onConstitutionSavingThrows,
)
}
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
SavingsThrows(
savingsThrows = sheet.intelligenceSavingThrows,
onClick = onIntelligenceSavingThrows,
)
SavingsThrows(
savingsThrows = sheet.wisdomSavingThrows,
onClick = onWisdomSavingThrows,
)
SavingsThrows(
savingsThrows = sheet.charismaSavingThrows,
onClick = onCharismaSavingThrows,
)
}
}
SectionTitle(
modifier = Modifier.padding(top = 16.dp),
title = R.string.character_sheet_title_proficiencies,
)
Column(
ProficiencyPage(
modifier = Modifier
.padding(horizontal = 16.dp)
.ddBorder(
inner = remember { RoundedCornerShape(size = 8.dp) },
outline = remember { CutCornerShape(size = 16.dp) },
),
) {
Proficiency(
proficiency = sheet.acrobatics,
onClick = onAcrobatics,
)
Proficiency(
proficiency = sheet.animalHandling,
onClick = onAnimalHandling,
)
Proficiency(
proficiency = sheet.arcana,
onClick = onArcana,
)
Proficiency(
proficiency = sheet.athletics,
onClick = onAthletics,
)
Proficiency(
proficiency = sheet.deception,
onClick = onDeception,
)
Proficiency(
proficiency = sheet.history,
onClick = onHistory,
)
Proficiency(
proficiency = sheet.insight,
onClick = onInsight,
)
Proficiency(
proficiency = sheet.intimidation,
onClick = onIntimidation,
)
Proficiency(
proficiency = sheet.investigation,
onClick = onInvestigation,
)
Proficiency(
proficiency = sheet.medicine,
onClick = onMedicine,
)
Proficiency(
proficiency = sheet.nature,
onClick = onNature,
)
Proficiency(
proficiency = sheet.perception,
onClick = onPerception,
)
Proficiency(
proficiency = sheet.performance,
onClick = onPerformance,
)
Proficiency(
proficiency = sheet.persuasion,
onClick = onPersuasion,
)
Proficiency(
proficiency = sheet.religion,
onClick = onReligion,
)
Proficiency(
proficiency = sheet.sleightOfHand,
onClick = onSleightOfHand,
)
Proficiency(
proficiency = sheet.stealth,
onClick = onStealth,
)
Proficiency(
proficiency = sheet.survival,
onClick = onSurvival,
)
}
.fillMaxWidth()
.padding(horizontal = 16.dp),
sheet = sheet,
onInitiative = onInitiative,
onStats = onStats,
onProficiencies = onProficiencies,
)
if (actions.value.isNotEmpty() || counter.value.isNotEmpty()) {
Row(
@ -657,11 +314,6 @@ private fun CharacterSheetContent(
}
if (alterations.value.isNotEmpty()) {
SectionTitle(
modifier = Modifier.padding(top = 16.dp),
title = R.string.character_sheet_title_alteration,
)
Column(
modifier = Modifier
.padding(horizontal = 16.dp)
@ -697,45 +349,6 @@ private fun CharacterSheetContent(
}
}
@Composable
private fun SectionTitle(
modifier: Modifier = Modifier,
@StringRes title: Int
) {
Row(
modifier = modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
) {
Image(
modifier = Modifier.graphicsLayer { rotationY = 180f },
painter = painterResource(id = R.drawable.art_clip_1),
contentScale = ContentScale.FillWidth,
alignment = Alignment.Center,
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
contentDescription = null,
)
Text(
modifier = Modifier
.weight(weight = 1f, fill = false)
.padding(horizontal = 8.dp),
style = MaterialTheme.typography.titleLarge,
textAlign = TextAlign.Center,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = stringResource(title),
)
Image(
painter = painterResource(id = R.drawable.art_clip_1),
contentScale = ContentScale.FillWidth,
alignment = Alignment.Center,
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
contentDescription = null,
)
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, heightDp = 2000)
@ -747,185 +360,176 @@ private fun CharacterScreenPreview() {
armorClass = LabelPointUio(
label = R.string.character_sheet_title_ca,
value = "15",
max = null,
),
hitPoint = LabelPointUio(
label = R.string.character_sheet_title_hp,
value = "13",
max = "15",
value = "125",
max = "/ 150",
),
speed = LabelPointUio(
label = R.string.character_sheet_title_speed,
value = "30",
max = "m",
),
initiative = LabelPointUio(
label = R.string.character_sheet_title_initiative,
value = "+2",
max = null,
),
strength = StatUio(
label = R.string.character_sheet_stat_strength,
value = 16,
modifier = 3,
stats = listOf(
StatUio(
id = StatUio.ID.STRENGTH,
value = 16,
modifier = 3,
),
StatUio(
id = StatUio.ID.DEXTERITY,
value = 10,
modifier = 0,
),
StatUio(
id = StatUio.ID.CONSTITUTION,
value = 16,
modifier = 3,
),
StatUio(
id = StatUio.ID.INTELLIGENCE,
value = 8,
modifier = 1,
),
StatUio(
id = StatUio.ID.WISDOM,
value = 14,
modifier = 2,
),
StatUio(
id = StatUio.ID.CHARISMA,
value = 10,
modifier = 0,
),
),
dexterity = StatUio(
label = R.string.character_sheet_stat_dexterity,
value = 10,
modifier = 0,
savingThrows = listOf(
ProficiencyUio(
id = ProficiencyUio.ID.STRENGTH_SAVING_THROW,
multiplier = 1,
modifier = 5,
),
ProficiencyUio(
id = ProficiencyUio.ID.DEXTERITY_SAVING_THROW,
multiplier = 0,
modifier = 0,
),
ProficiencyUio(
id = ProficiencyUio.ID.CONSTITUTION_SAVING_THROW,
multiplier = 1,
modifier = +5,
),
ProficiencyUio(
id = ProficiencyUio.ID.INTELLIGENCE_SAVING_THROW,
multiplier = 0,
modifier = -1,
),
ProficiencyUio(
id = ProficiencyUio.ID.WISDOM_SAVING_THROW,
multiplier = 0,
modifier = +2,
),
ProficiencyUio(
id = ProficiencyUio.ID.CHARISMA_SAVING_THROW,
multiplier = 0,
modifier = +0,
),
),
constitution = StatUio(
label = R.string.character_sheet_stat_constitution,
value = 16,
modifier = 3,
),
intelligence = StatUio(
label = R.string.character_sheet_stat_intelligence,
value = 8,
modifier = 1,
),
wisdom = StatUio(
label = R.string.character_sheet_stat_wisdom,
value = 14,
modifier = 2,
),
charisma = StatUio(
label = R.string.character_sheet_stat_charisma,
value = 10,
modifier = 0
),
strengthSavingThrows = SavingsThrowsUio(
label = R.string.character_sheet_stat_strength,
multiplier = 1,
modifier = 5,
),
dexteritySavingThrows = SavingsThrowsUio(
label = R.string.character_sheet_stat_dexterity,
multiplier = 0,
modifier = 0,
),
constitutionSavingThrows = SavingsThrowsUio(
label = R.string.character_sheet_stat_constitution,
multiplier = 1,
modifier = +5,
),
intelligenceSavingThrows = SavingsThrowsUio(
label = R.string.character_sheet_stat_intelligence,
multiplier = 0,
modifier = -1,
),
wisdomSavingThrows = SavingsThrowsUio(
label = R.string.character_sheet_stat_wisdom,
multiplier = 0,
modifier = +2,
),
charismaSavingThrows = SavingsThrowsUio(
label = R.string.character_sheet_stat_charisma,
multiplier = 0,
modifier = +0,
),
acrobatics = ProficiencyUio(
label = R.string.character_sheet_proficiency_acrobatics,
related = R.string.character_sheet_stat_dexterity_short,
multiplier = 0,
modifier = +0,
),
animalHandling = ProficiencyUio(
label = R.string.character_sheet_proficiency_animal_handling,
related = R.string.character_sheet_stat_wisdom_short,
multiplier = 0,
modifier = 2,
),
arcana = ProficiencyUio(
label = R.string.character_sheet_proficiency_arcana,
related = R.string.character_sheet_stat_intelligence_short,
multiplier = 0,
modifier = -1,
),
athletics = ProficiencyUio(
label = R.string.character_sheet_proficiency_athletics,
related = R.string.character_sheet_stat_strength_short,
multiplier = 1,
modifier = 5,
),
deception = ProficiencyUio(
label = R.string.character_sheet_proficiency_deception,
related = R.string.character_sheet_stat_charisma_short,
multiplier = 0,
modifier = 0,
),
history = ProficiencyUio(
label = R.string.character_sheet_proficiency_history,
related = R.string.character_sheet_stat_intelligence_short,
multiplier = 0,
modifier = -1,
),
insight = ProficiencyUio(
label = R.string.character_sheet_proficiency_insight,
related = R.string.character_sheet_stat_wisdom_short,
multiplier = 1,
modifier = 4,
),
intimidation = ProficiencyUio(
label = R.string.character_sheet_proficiency_intimidation,
related = R.string.character_sheet_stat_charisma_short,
multiplier = 1,
modifier = 2,
),
investigation = ProficiencyUio(
label = R.string.character_sheet_proficiency_investigation,
related = R.string.character_sheet_stat_intelligence_short,
multiplier = 0,
modifier = -1,
),
medicine = ProficiencyUio(
label = R.string.character_sheet_proficiency_medicine,
related = R.string.character_sheet_stat_wisdom_short,
multiplier = 0,
modifier = 2,
),
nature = ProficiencyUio(
label = R.string.character_sheet_proficiency_nature,
related = R.string.character_sheet_stat_intelligence_short,
multiplier = 0,
modifier = -1,
),
perception = ProficiencyUio(
label = R.string.character_sheet_proficiency_perception,
related = R.string.character_sheet_stat_wisdom_short,
multiplier = 1,
modifier = 4,
),
performance = ProficiencyUio(
label = R.string.character_sheet_proficiency_performance,
related = R.string.character_sheet_stat_charisma_short,
multiplier = 0,
modifier = 0,
),
persuasion = ProficiencyUio(
label = R.string.character_sheet_proficiency_persuasion,
related = R.string.character_sheet_stat_charisma_short,
multiplier = 0,
modifier = 0,
),
religion = ProficiencyUio(
label = R.string.character_sheet_proficiency_religion,
related = R.string.character_sheet_stat_intelligence_short,
multiplier = 0,
modifier = -1,
),
sleightOfHand = ProficiencyUio(
label = R.string.character_sheet_proficiency_sleight_of_hand,
related = R.string.character_sheet_stat_dexterity_short,
multiplier = 0,
modifier = 0,
),
stealth = ProficiencyUio(
label = R.string.character_sheet_proficiency_stealth,
related = R.string.character_sheet_stat_dexterity_short,
multiplier = 0,
modifier = 0,
),
survival = ProficiencyUio(
label = R.string.character_sheet_proficiency_survival,
related = R.string.character_sheet_stat_wisdom_short,
multiplier = 1,
modifier = 4,
proficiencies = listOf(
ProficiencyUio(
id = ProficiencyUio.ID.ACROBATICS,
multiplier = 0,
modifier = +0,
),
ProficiencyUio(
id = ProficiencyUio.ID.ANIMAL_HANDLING,
multiplier = 0,
modifier = 2,
),
ProficiencyUio(
id = ProficiencyUio.ID.ARCANA,
multiplier = 0,
modifier = -1,
),
ProficiencyUio(
id = ProficiencyUio.ID.ATHLETICS,
multiplier = 1,
modifier = 5,
),
ProficiencyUio(
id = ProficiencyUio.ID.DECEPTION,
multiplier = 0,
modifier = 0,
),
ProficiencyUio(
id = ProficiencyUio.ID.HISTORY,
multiplier = 0,
modifier = -1,
),
ProficiencyUio(
id = ProficiencyUio.ID.INSIGHT,
multiplier = 1,
modifier = 4,
),
ProficiencyUio(
id = ProficiencyUio.ID.INTIMIDATION,
multiplier = 1,
modifier = 2,
),
ProficiencyUio(
id = ProficiencyUio.ID.INVESTIGATION,
multiplier = 0,
modifier = -1,
),
ProficiencyUio(
id = ProficiencyUio.ID.MEDICINE,
multiplier = 0,
modifier = 2,
),
ProficiencyUio(
id = ProficiencyUio.ID.NATURE,
multiplier = 0,
modifier = -1,
),
ProficiencyUio(
id = ProficiencyUio.ID.PERCEPTION,
multiplier = 1,
modifier = 4,
),
ProficiencyUio(
id = ProficiencyUio.ID.PERFORMANCE,
multiplier = 0,
modifier = 0,
),
ProficiencyUio(
id = ProficiencyUio.ID.PERSUASION,
multiplier = 0,
modifier = 0,
),
ProficiencyUio(
id = ProficiencyUio.ID.RELIGION,
multiplier = 0,
modifier = -1,
),
ProficiencyUio(
id = ProficiencyUio.ID.SLEIGHT_OF_HAND,
multiplier = 0,
modifier = 0,
),
ProficiencyUio(
id = ProficiencyUio.ID.STEALTH,
multiplier = 0,
modifier = 0,
),
ProficiencyUio(
id = ProficiencyUio.ID.SURVIVAL,
multiplier = 1,
modifier = 4,
),
),
)
}
@ -970,36 +574,8 @@ private fun CharacterScreenPreview() {
},
onBack = { },
onInitiative = { },
onStrength = { },
onDexterity = { },
onConstitution = { },
onIntelligence = { },
onWisdom = { },
onCharisma = { },
onStrengthSavingThrows = { },
onDexteritySavingThrows = { },
onConstitutionSavingThrows = { },
onIntelligenceSavingThrows = { },
onWisdomSavingThrows = { },
onCharismaSavingThrows = { },
onAcrobatics = { },
onAnimalHandling = { },
onArcana = { },
onAthletics = { },
onDeception = { },
onHistory = { },
onInsight = { },
onIntimidation = { },
onInvestigation = { },
onMedicine = { },
onNature = { },
onPerception = { },
onPerformance = { },
onPersuasion = { },
onReligion = { },
onSleightOfHand = { },
onStealth = { },
onSurvival = { },
onStats = { },
onProficiencies = { },
onHit = { },
onDamage = { },
)

View file

@ -0,0 +1,185 @@
package com.pixelized.rplexicon.ui.screens.character
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.character.composable.Proficiency
import com.pixelized.rplexicon.ui.screens.character.composable.ProficiencyUio
import com.pixelized.rplexicon.ui.screens.character.composable.Stat
import com.pixelized.rplexicon.ui.screens.character.composable.StatUio
import com.pixelized.rplexicon.utilitary.extentions.ddBorder
@Composable
fun ProficiencyPage(
modifier: Modifier = Modifier,
inner: Shape = remember { RoundedCornerShape(size = 8.dp) },
outline: Shape = remember { CutCornerShape(size = 16.dp) },
sheet: CharacterSheetUio,
onInitiative: () -> Unit,
onStats: (StatUio) -> Unit,
onProficiencies: (ProficiencyUio) -> Unit,
) {
ProficiencyLayout(
modifier = modifier,
inner = inner,
outline = outline,
stats = {
Column(
modifier = Modifier
.sizeIn(minWidth = 100.dp, minHeight = 116.dp)
.ddBorder(outline = outline, inner = inner)
.clickable(onClick = onInitiative)
.padding(all = 8.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(
style = MaterialTheme.typography.labelSmall,
text = sheet.initiative.label?.let { stringResource(id = it) } ?: ""
)
Text(
style = MaterialTheme.typography.displayMedium,
text = sheet.initiative.value ?: ""
)
}
sheet.stats.forEach {
Stat(
modifier = Modifier.ddBorder(inner = inner, outline = outline),
stat = it,
onClick = onStats,
)
}
},
savingThrows = {
sheet.savingThrows.forEach {
Proficiency(
proficiency = it,
onClick = onProficiencies,
)
}
Text(
modifier = Modifier.padding(vertical = 4.dp),
style = MaterialTheme.typography.labelSmall,
text = stringResource(id = R.string.character_sheet_title_saving_throws)
)
},
proficiencies = {
sheet.proficiencies.forEach {
Proficiency(
proficiency = it,
onClick = onProficiencies,
)
}
Text(
modifier = Modifier.padding(vertical = 4.dp),
style = MaterialTheme.typography.labelSmall,
text = stringResource(id = R.string.character_sheet_title_proficiencies)
)
},
)
}
@Composable
private fun ProficiencyLayout(
modifier: Modifier = Modifier,
spacing: Dp = 16.dp,
inner: Shape,
outline: Shape,
stats: @Composable ColumnScope.() -> Unit,
savingThrows: @Composable ColumnScope.() -> Unit,
proficiencies: @Composable ColumnScope.() -> Unit,
) {
val density = LocalDensity.current
Layout(
modifier = modifier,
content = {
Column(
modifier = Modifier.layoutId("StatsId"),
content = stats,
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween,
)
Column(
modifier = Modifier
.layoutId("SavingThrowsId")
.ddBorder(inner = inner, outline = outline),
horizontalAlignment = Alignment.CenterHorizontally,
content = savingThrows,
)
Column(
modifier = Modifier
.layoutId("ProficienciesId")
.ddBorder(inner = inner, outline = outline),
horizontalAlignment = Alignment.CenterHorizontally,
content = proficiencies,
)
},
measurePolicy = { measurables, constraints ->
val spacingPx = with(density) { spacing.toPx().toInt() }
val statsWidth = with(density) { 100.dp.toPx().toInt() }
val proficienciesWidth = constraints.maxWidth - statsWidth - spacingPx
val savingThrowsMeasure = measurables.savingThrows.measure(
constraints.copy(
minWidth = proficienciesWidth,
maxWidth = proficienciesWidth,
)
)
val proficienciesMeasure = measurables.proficiencies.measure(
constraints.copy(
minWidth = proficienciesWidth,
maxWidth = proficienciesWidth,
)
)
val statsHeight = savingThrowsMeasure.height + proficienciesMeasure.height + spacingPx
val statsMeasure = measurables.stats.measure(
constraints.copy(
minWidth = statsWidth,
maxWidth = statsWidth,
minHeight = statsHeight,
maxHeight = statsHeight,
)
)
layout(width = constraints.maxWidth, height = statsMeasure.height) {
statsMeasure.place(
x = 0,
y = 0,
)
savingThrowsMeasure.place(
x = statsWidth + spacingPx,
y = 0,
)
proficienciesMeasure.place(
x = statsWidth + spacingPx,
y = savingThrowsMeasure.height + spacingPx,
)
}
}
)
}
val List<Measurable>.stats get() = first { it.layoutId == "StatsId" }
val List<Measurable>.savingThrows get() = first { it.layoutId == "SavingThrowsId" }
val List<Measurable>.proficiencies get() = first { it.layoutId == "ProficienciesId" }

View file

@ -2,22 +2,16 @@ package com.pixelized.rplexicon.ui.screens.character.composable
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@ -26,65 +20,49 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.ddBorder
@Stable
class LabelPointUio(
val label: Int?,
val value: String?,
val max: String?,
val max: String? = null,
)
@Composable
fun LabelPoint(
modifier: Modifier = Modifier,
style: TextStyle = MaterialTheme.typography.displayMedium,
hp: LabelPointUio,
onClick: (() -> Unit)? = null
titleStyle: TextStyle = MaterialTheme.typography.labelSmall,
valueStyle: TextStyle = MaterialTheme.typography.headlineMedium,
labelStyle: TextStyle = MaterialTheme.typography.titleMedium,
label: LabelPointUio,
) {
Column(
modifier = modifier
.sizeIn(minWidth = 96.dp, minHeight = 80.dp)
.ddBorder(
horizontalSpacing = 4.dp,
verticalSpacing = 4.dp,
outline = remember {
CutCornerShape(
topStart = 16.dp,
topEnd = 16.dp,
bottomEnd = 38.dp,
bottomStart = 38.dp
)
},
inner = remember {
CutCornerShape(
topStart = 14.dp,
topEnd = 14.dp,
bottomEnd = 36.dp,
bottomStart = 36.dp
)
},
)
.clickable(enabled = onClick != null) { onClick?.let { it() } }
.padding(all = 8.dp),
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier.padding(all = 8.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
style = MaterialTheme.typography.labelSmall,
text = hp.label?.let { stringResource(id = it) } ?: ""
)
Text(
style = style,
text = hp.value ?: ""
)
Divider(
modifier = Modifier.width(width = 32.dp),
)
Text(
style = MaterialTheme.typography.labelSmall,
text = hp.max ?: ""
)
label.label?.let {
Text(
style = titleStyle,
text = stringResource(id = it)
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(
modifier = Modifier.alignByBaseline(),
style = valueStyle,
text = label.value ?: "0"
)
label.max?.let {
Text(
modifier = Modifier.alignByBaseline(),
style = labelStyle,
text = it,
)
}
}
}
}
@ -100,26 +78,24 @@ private fun LabelPointPreview() {
horizontalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterHorizontally),
) {
LabelPoint(
style = MaterialTheme.typography.displaySmall,
hp = LabelPointUio(
label = R.string.character_sheet_title_hp,
label = LabelPointUio(
label = R.string.character_sheet_title_ca,
value = "15",
max = null
),
)
LabelPoint(
hp = LabelPointUio(
label = R.string.character_sheet_title_ca,
label = LabelPointUio(
label = R.string.character_sheet_title_hp,
value = "13",
max = "15"
max = "/ 15"
),
)
LabelPoint(
style = MaterialTheme.typography.displaySmall,
hp = LabelPointUio(
label = R.string.character_sheet_title_initiative,
value = "+0",
max = null
label = LabelPointUio(
label = R.string.character_sheet_title_speed,
value = "10",
max = "m"
),
)
}

View file

@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.utilitary.extentions.dashedBorder
@Composable
fun MasteryCircle(
@ -22,17 +23,22 @@ fun MasteryCircle(
0 -> Box(
modifier = modifier
.size(size = size)
.border(
.dashedBorder(
width = 1.dp,
color = MaterialTheme.colorScheme.onSurface,
shape = CircleShape
shape = CircleShape,
on = 2.dp,
off = 1.dp
)
)
1 -> Box(
modifier = modifier
.size(size = size)
.background(color = MaterialTheme.colorScheme.onSurface, shape = CircleShape)
.background(
color = MaterialTheme.colorScheme.onSurface,
shape = CircleShape,
)
)
else -> Box(

View file

@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
@ -18,6 +17,7 @@ import androidx.compose.runtime.Stable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
@ -28,60 +28,162 @@ import com.pixelized.rplexicon.utilitary.extentions.toLabel
@Stable
data class ProficiencyUio(
@StringRes val label: Int, // the proficiency label
@StringRes val related: Int, // the related stat to the proficiency
val id: ID,
val multiplier: Int, // the multiplier (use for the circle) 0 -> None 1 -> Mastery 2 -> Expertise
val modifier: Int, // the proficiency value. (stats + mastery)
)
) {
enum class ID(
@StringRes val label: Int, // the proficiency label
@StringRes val related: Int?, // the related stat to the proficiency
) {
STRENGTH_SAVING_THROW(
label = R.string.character_sheet_stat_strength,
related = null,
),
DEXTERITY_SAVING_THROW(
label = R.string.character_sheet_stat_dexterity,
related = null,
),
CONSTITUTION_SAVING_THROW(
label = R.string.character_sheet_stat_constitution,
related = null,
),
INTELLIGENCE_SAVING_THROW(
label = R.string.character_sheet_stat_intelligence,
related = null,
),
WISDOM_SAVING_THROW(
label = R.string.character_sheet_stat_wisdom,
related = null,
),
CHARISMA_SAVING_THROW(
label = R.string.character_sheet_stat_charisma,
related = null,
),
ACROBATICS(
label = R.string.character_sheet_proficiency_acrobatics,
related = R.string.character_sheet_stat_dexterity_short,
),
ANIMAL_HANDLING(
label = R.string.character_sheet_proficiency_animal_handling,
related = R.string.character_sheet_stat_wisdom_short,
),
ARCANA(
label = R.string.character_sheet_proficiency_arcana,
related = R.string.character_sheet_stat_intelligence_short,
),
ATHLETICS(
label = R.string.character_sheet_proficiency_athletics,
related = R.string.character_sheet_stat_strength_short,
),
DECEPTION(
label = R.string.character_sheet_proficiency_deception,
related = R.string.character_sheet_stat_charisma_short,
),
HISTORY(
label = R.string.character_sheet_proficiency_history,
related = R.string.character_sheet_stat_intelligence_short,
),
INSIGHT(
label = R.string.character_sheet_proficiency_insight,
related = R.string.character_sheet_stat_wisdom_short,
),
INTIMIDATION(
label = R.string.character_sheet_proficiency_intimidation,
related = R.string.character_sheet_stat_charisma_short,
),
INVESTIGATION(
label = R.string.character_sheet_proficiency_investigation,
related = R.string.character_sheet_stat_intelligence_short,
),
MEDICINE(
label = R.string.character_sheet_proficiency_medicine,
related = R.string.character_sheet_stat_wisdom_short,
),
NATURE(
label = R.string.character_sheet_proficiency_nature,
related = R.string.character_sheet_stat_intelligence_short,
),
PERCEPTION(
label = R.string.character_sheet_proficiency_perception,
related = R.string.character_sheet_stat_wisdom_short,
),
PERFORMANCE(
label = R.string.character_sheet_proficiency_performance,
related = R.string.character_sheet_stat_charisma_short,
),
PERSUASION(
label = R.string.character_sheet_proficiency_persuasion,
related = R.string.character_sheet_stat_charisma_short,
),
RELIGION(
label = R.string.character_sheet_proficiency_religion,
related = R.string.character_sheet_stat_intelligence_short,
),
SLEIGHT_OF_HAND(
label = R.string.character_sheet_proficiency_sleight_of_hand,
related = R.string.character_sheet_stat_dexterity_short,
),
STEALTH(
label = R.string.character_sheet_proficiency_stealth,
related = R.string.character_sheet_stat_dexterity_short,
),
SURVIVAL(
label = R.string.character_sheet_proficiency_survival,
related = R.string.character_sheet_stat_wisdom_short,
),
}
}
@Composable
fun Proficiency(
modifier: Modifier = Modifier,
padding: PaddingValues = PaddingValues(horizontal = 8.dp),
padding: PaddingValues = PaddingValues(top = 8.dp, bottom = 8.dp, start = 4.dp, end = 8.dp),
proficiency: ProficiencyUio,
onClick: () -> Unit,
onClick: (ProficiencyUio) -> Unit,
) {
Box(
Row(
modifier = modifier
.heightIn(48.dp)
.clickable(onClick = onClick),
contentAlignment = Alignment.CenterStart,
.clickable { onClick(proficiency) }
.padding(paddingValues = padding),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
) {
Row(
modifier = Modifier.padding(paddingValues = padding),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(16.dp),
Box(
modifier = Modifier.size(size = 24.dp),
contentAlignment = Alignment.Center,
) {
Box(
modifier = Modifier.size(size = 24.dp),
contentAlignment = Alignment.Center,
) {
MasteryCircle(
multiplier = proficiency.multiplier,
)
}
MasteryCircle(
multiplier = proficiency.multiplier,
)
}
Row(
modifier = Modifier.weight(1f),
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.bodyMedium,
text = stringResource(id = proficiency.label),
)
Row(
modifier = Modifier.weight(1f),
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.bodySmall,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = stringResource(id = proficiency.id.label),
)
proficiency.id.related?.let { res ->
Text(
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.labelSmall,
text = "(${stringResource(proficiency.related)})",
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = "(${stringResource(res)})",
)
}
Text(
style = MaterialTheme.typography.titleMedium,
text = proficiency.modifier.toLabel(),
)
}
Text(
style = MaterialTheme.typography.titleMedium,
text = proficiency.modifier.toLabel(),
)
}
}
@ -104,20 +206,17 @@ private fun ProficiencyPreview(
private class ProficiencyPreviewProvider : PreviewParameterProvider<ProficiencyUio> {
override val values: Sequence<ProficiencyUio> = sequenceOf(
ProficiencyUio(
label = R.string.character_sheet_proficiency_acrobatics,
related = R.string.character_sheet_stat_dexterity_short,
id = ProficiencyUio.ID.ACROBATICS,
multiplier = 0,
modifier = -1
modifier = -1,
),
ProficiencyUio(
label = R.string.character_sheet_proficiency_athletics,
related = R.string.character_sheet_stat_strength_short,
id = ProficiencyUio.ID.ATHLETICS,
multiplier = 1,
modifier = 5,
),
ProficiencyUio(
label = R.string.character_sheet_proficiency_sleight_of_hand,
related = R.string.character_sheet_stat_dexterity_short,
id = ProficiencyUio.ID.SLEIGHT_OF_HAND,
multiplier = 2,
modifier = 8,
),

View file

@ -1,123 +0,0 @@
package com.pixelized.rplexicon.ui.screens.character.composable
import android.content.res.Configuration
import androidx.annotation.StringRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.ddBorder
import com.pixelized.rplexicon.utilitary.extentions.toLabel
@Stable
data class SavingsThrowsUio(
@StringRes val label: Int, // the proficiency label
val multiplier: Int, // the multiplier (use for the circle) 0 -> None 1 -> Mastery 2 -> Expertise
val modifier: Int, // the proficiency value. (stats + mastery)
)
@Composable
fun SavingsThrows(
modifier: Modifier = Modifier,
padding: PaddingValues = PaddingValues(start = 8.dp, top = 8.dp, bottom = 8.dp, end = 8.dp),
savingsThrows: SavingsThrowsUio,
onClick: () -> Unit,
) {
Row(
modifier = modifier
.ddBorder(
horizontalSpacing = 3.dp,
outline = remember { CutCornerShape(size = 24.dp) },
inner = remember { RoundedCornerShape(size = 24.dp) },
)
.clickable(onClick = onClick)
.padding(paddingValues = padding),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Box(
modifier = Modifier.size(size = 24.dp),
contentAlignment = Alignment.Center,
) {
MasteryCircle(
multiplier = savingsThrows.multiplier,
)
}
Text(
modifier = Modifier.weight(weight = 1f, fill = true),
style = MaterialTheme.typography.bodyMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
text = stringResource(id = savingsThrows.label),
)
Text(
style = MaterialTheme.typography.titleMedium,
text = savingsThrows.modifier.toLabel(),
)
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
fun SavingsThrowsPreview(
@PreviewParameter(SavingsThrowsPreviewProvider::class) preview: SavingsThrowsUio,
) {
LexiconTheme {
Surface {
SavingsThrows(
modifier = Modifier.padding(all = 8.dp),
savingsThrows = preview,
onClick = { },
)
}
}
}
private class SavingsThrowsPreviewProvider : PreviewParameterProvider<SavingsThrowsUio> {
override val values: Sequence<SavingsThrowsUio> = sequenceOf(
SavingsThrowsUio(
label = R.string.character_sheet_stat_strength,
multiplier = 0,
modifier = 0,
),
SavingsThrowsUio(
label = R.string.character_sheet_stat_intelligence,
multiplier = 1,
modifier = 5,
),
SavingsThrowsUio(
label = R.string.character_sheet_stat_dexterity,
multiplier = 2,
modifier = 8,
),
SavingsThrowsUio(
label = R.string.character_sheet_stat_charisma,
multiplier = 0,
modifier = -1,
),
)
}

View file

@ -7,6 +7,7 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
@ -29,31 +30,39 @@ import com.pixelized.rplexicon.utilitary.extentions.toLabel
@Stable
data class StatUio(
@StringRes val label: Int,
val id: ID,
val value: Int,
val modifier: Int,
)
) {
enum class ID(
@StringRes val label: Int,
) {
STRENGTH(label = R.string.character_sheet_stat_strength),
DEXTERITY(label = R.string.character_sheet_stat_dexterity),
CONSTITUTION(label = R.string.character_sheet_stat_constitution),
INTELLIGENCE(label = R.string.character_sheet_stat_intelligence),
WISDOM(label = R.string.character_sheet_stat_wisdom),
CHARISMA(label = R.string.character_sheet_stat_charisma),
}
}
@Composable
fun Stat(
modifier: Modifier = Modifier,
stat: StatUio,
onClick: () -> Unit
onClick: (StatUio) -> Unit,
) {
Column(
modifier = modifier
.ddBorder(
inner = remember { RoundedCornerShape(size = 8.dp) },
outline = remember { CutCornerShape(size = 16.dp) },
)
.clickable(onClick = onClick)
.sizeIn(minWidth = 100.dp, minHeight = 130.dp)
.clickable { onClick(stat) }
.padding(all = 8.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(4.dp),
verticalArrangement = Arrangement.spacedBy(4.dp, Alignment.CenterVertically),
) {
Text(
style = MaterialTheme.typography.labelSmall,
text = stringResource(id = stat.label),
text = stringResource(id = stat.id.label),
)
Text(
style = MaterialTheme.typography.displayMedium,
@ -76,9 +85,14 @@ private fun StatPreview() {
LexiconTheme {
Surface {
Stat(
modifier = Modifier.padding(all = 8.dp),
modifier = Modifier
.padding(all = 8.dp)
.ddBorder(
inner = remember { RoundedCornerShape(size = 8.dp) },
outline = remember { CutCornerShape(size = 16.dp) },
),
stat = StatUio(
label = R.string.character_sheet_stat_constitution,
id = StatUio.ID.CONSTITUTION,
value = 16,
modifier = 3,
),

View file

@ -17,7 +17,7 @@ class LexiconColors(
val status: Color,
val navigation: Color,
val handle: Color,
val rollOverlayBrush: Brush,
val rollOverlayBrush: Color,
val dice: Dice,
val characterSheet: CharacterSheet,
) {
@ -82,12 +82,7 @@ fun colorScheme(
shadow: Color = ShadowPalette.shadow,
status: Color = Color.Transparent,
navigation: Color = Color.Transparent,
rollOverlayBrush: Brush = Brush.radialGradient(
colors = listOf(
base.surface.copy(alpha = 0.75f),
base.surface.copy(alpha = 0.25f),
),
),
rollOverlayBrush: Color = base.surface.copy(alpha = 0.5f),
dice: LexiconColors.Dice = LexiconColors.Dice(
tint = base.primary,
value = base.onSurface,

View file

@ -23,6 +23,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.platform.LocalDensity

View file

@ -0,0 +1,195 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.pixelized.rplexicon.utilitary.extentions
import androidx.compose.foundation.BorderStroke
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.geometry.isSimple
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.drawscope.withTransform
import androidx.compose.ui.unit.Dp
/**
* Modify element to add border with appearance specified with a [border] and a [shape], pad the
* content by the [BorderStroke.width] and clip it.
*
* @sample androidx.compose.foundation.samples.BorderSample()
*
* @param border [BorderStroke] class that specifies border appearance, such as size and color
* @param shape shape of the border
*/
fun Modifier.dashedBorder(border: BorderStroke, shape: Shape = RectangleShape, on: Dp, off: Dp) =
dashedBorder(width = border.width, brush = border.brush, shape = shape, on, off)
/**
* Returns a [Modifier] that adds border with appearance specified with [width], [color] and a
* [shape], pads the content by the [width] and clips it.
*
* @sample androidx.compose.foundation.samples.BorderSampleWithDataClass()
*
* @param width width of the border. Use [Dp.Hairline] for a hairline border.
* @param color color to paint the border with
* @param shape shape of the border
* @param on the size of the solid part of the dashes
* @param off the size of the space between dashes
*/
fun Modifier.dashedBorder(width: Dp, color: Color, shape: Shape = RectangleShape, on: Dp, off: Dp) =
dashedBorder(width, SolidColor(color), shape, on, off)
/**
* Returns a [Modifier] that adds border with appearance specified with [width], [brush] and a
* [shape], pads the content by the [width] and clips it.
*
* @sample androidx.compose.foundation.samples.BorderSampleWithBrush()
*
* @param width width of the border. Use [Dp.Hairline] for a hairline border.
* @param brush brush to paint the border with
* @param shape shape of the border
*/
fun Modifier.dashedBorder(width: Dp, brush: Brush, shape: Shape, on: Dp, off: Dp): Modifier =
this.then(
Modifier.drawWithCache {
val outline: Outline = shape.createOutline(size, layoutDirection, this)
val borderSize = if (width == Dp.Hairline) 1f else width.toPx()
var insetOutline: Outline? = null // outline used for roundrect/generic shapes
var stroke: Stroke? = null // stroke to draw border for all outline types
var pathClip: Path? = null // path to clip roundrect/generic shapes
var inset = 0f // inset to translate before drawing the inset outline
// path to draw generic shapes or roundrects with different corner radii
var insetPath: Path? = null
if (borderSize > 0 && size.minDimension > 0f) {
if (outline is Outline.Rectangle) {
stroke = Stroke(
borderSize, pathEffect = PathEffect.dashPathEffect(
floatArrayOf(on.toPx(), off.toPx())
)
)
} else {
// Multiplier to apply to the border size to get a stroke width that is
// large enough to cover the corners while not being too large to overly
// square off the internal shape. The resultant shape will be
// clipped to the desired shape. Any value lower will show artifacts in
// the corners of shapes. A value too large will always square off
// the internal shape corners. For example, for a rounded rect border
// a large multiplier will always have squared off edges within the
// inner section of the stroke, however, having a smaller multiplier
// will still keep the rounded effect for the inner section of the
// border
val strokeWidth = 1.2f * borderSize
inset = borderSize - strokeWidth / 2
val insetSize = Size(
size.width - inset * 2,
size.height - inset * 2
)
insetOutline = shape.createOutline(insetSize, layoutDirection, this)
stroke = Stroke(
strokeWidth, pathEffect = PathEffect.dashPathEffect(
floatArrayOf(on.toPx(), off.toPx())
)
)
pathClip = when (outline) {
is Outline.Rounded -> {
Path().apply { addRoundRect(outline.roundRect) }
}
is Outline.Generic -> {
outline.path
}
// should not get here because we check for Outline.Rectangle above
else -> {
null
}
}
insetPath = when {
// Rounded rect with non equal corner radii needs a path to be pre-translated
insetOutline is Outline.Rounded && !insetOutline.roundRect.isSimple -> {
Path().apply {
addRoundRect(insetOutline.roundRect)
translate(Offset(inset, inset))
}
}
// Generic paths must be created and pre-translated
insetOutline is Outline.Generic -> {
Path().apply {
addPath(insetOutline.path, Offset(inset, inset))
}
}
// Drawing a round rect with equal corner radii without usage of a path
else -> {
null
}
}
}
}
onDrawWithContent {
drawContent()
// Only draw the border if a have a valid stroke parameter. If we have
// an invalid border size we will just draw the content
if (stroke != null) {
if (insetOutline != null && pathClip != null) {
val isSimpleRoundRect =
insetOutline is Outline.Rounded && insetOutline.roundRect.isSimple
withTransform({
clipPath(pathClip)
// we are drawing the round rect not as a path so we must
// translate ourselves othe
if (isSimpleRoundRect) {
translate(inset, inset)
}
}) {
if (isSimpleRoundRect) {
// If we don't have an insetPath then we are drawing
// a simple round rect with the corner radii all identical
val rrect = (insetOutline as Outline.Rounded).roundRect
drawRoundRect(
brush = brush,
topLeft = Offset(rrect.left, rrect.top),
size = Size(rrect.width, rrect.height),
cornerRadius = rrect.topLeftCornerRadius,
style = stroke
)
} else if (insetPath != null) {
drawPath(insetPath, brush, style = stroke)
}
}
} else {
// Rectangular border fast path
val strokeWidth = stroke.width
val halfStrokeWidth = strokeWidth / 2
drawRect(
brush = brush,
topLeft = Offset(halfStrokeWidth, halfStrokeWidth),
size = Size(
size.width - strokeWidth,
size.height - strokeWidth
),
style = stroke
)
}
}
}
}
)