Right after refactoring
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
package com.kawomi.githugandroid
|
package com.kawomi.githugandroid
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.gestures.Orientation
|
import androidx.compose.foundation.gestures.Orientation
|
||||||
@@ -41,7 +40,6 @@ import androidx.compose.material3.Scaffold
|
|||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.darkColorScheme
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
@@ -74,130 +72,8 @@ import androidx.compose.ui.unit.sp
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
||||||
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
||||||
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
|
|
||||||
import androidx.datastore.preferences.core.edit
|
|
||||||
import androidx.datastore.preferences.core.emptyPreferences
|
|
||||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
|
||||||
import androidx.datastore.preferences.preferencesDataStoreFile
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.catch
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
private val AppBackground = Color(0xFF000000)
|
|
||||||
private val PanelPrimary = Color(0xFF121212)
|
|
||||||
private val PanelSecondary = Color(0xFF1E1E1E)
|
|
||||||
private val PanelTertiary = Color(0xFF262626)
|
|
||||||
private val TerminalBackground = Color(0xFF050505)
|
|
||||||
private val TextPrimary = Color(0xFFFFFFFF)
|
|
||||||
private val TextSecondary = Color(0xFFE6E6E6)
|
|
||||||
private val TextMuted = Color(0xFFBDBDBD)
|
|
||||||
private val Accent = Color(0xFF00E5FF)
|
|
||||||
private val Success = Color(0xFF00FF95)
|
|
||||||
|
|
||||||
private val GitHugColorScheme = darkColorScheme(
|
|
||||||
primary = Accent,
|
|
||||||
onPrimary = Color.Black,
|
|
||||||
secondary = TextPrimary,
|
|
||||||
onSecondary = Color.Black,
|
|
||||||
background = AppBackground,
|
|
||||||
onBackground = TextPrimary,
|
|
||||||
surface = PanelPrimary,
|
|
||||||
onSurface = TextPrimary,
|
|
||||||
surfaceVariant = PanelSecondary,
|
|
||||||
onSurfaceVariant = TextSecondary,
|
|
||||||
outline = TextMuted,
|
|
||||||
)
|
|
||||||
|
|
||||||
private enum class PaneId(val title: String) {
|
|
||||||
LEVELS("Levels"),
|
|
||||||
VISUAL("Visual"),
|
|
||||||
EXERCISE("Exercise"),
|
|
||||||
TERMINAL("Terminal"),
|
|
||||||
}
|
|
||||||
|
|
||||||
private data class PaneLayout(
|
|
||||||
val order: List<PaneId>,
|
|
||||||
val collapsed: Set<PaneId>,
|
|
||||||
val weights: Map<PaneId, Float>,
|
|
||||||
)
|
|
||||||
|
|
||||||
private enum class ExerciseDetailPanel {
|
|
||||||
HINT,
|
|
||||||
SUGGESTIONS,
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun defaultPaneLayout(): PaneLayout = PaneLayout(
|
|
||||||
order = listOf(PaneId.EXERCISE, PaneId.TERMINAL, PaneId.VISUAL, PaneId.LEVELS),
|
|
||||||
collapsed = emptySet(),
|
|
||||||
weights = mapOf(
|
|
||||||
PaneId.EXERCISE to 0.95f,
|
|
||||||
PaneId.TERMINAL to 1.45f,
|
|
||||||
PaneId.VISUAL to 1.0f,
|
|
||||||
PaneId.LEVELS to 0.8f,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
private class PaneLayoutStore(private val context: Context) {
|
|
||||||
private val dataStore = PreferenceDataStoreFactory.create(
|
|
||||||
produceFile = { context.preferencesDataStoreFile("pane_layout_preferences") }
|
|
||||||
)
|
|
||||||
|
|
||||||
private val orderKey = stringPreferencesKey("pane_order")
|
|
||||||
private val collapsedKey = stringPreferencesKey("pane_collapsed")
|
|
||||||
private val weightsKey = stringPreferencesKey("pane_weights")
|
|
||||||
|
|
||||||
val layoutFlow = dataStore.data
|
|
||||||
.catch { error ->
|
|
||||||
if (error is IOException) emit(emptyPreferences()) else throw error
|
|
||||||
}
|
|
||||||
.map { preferences ->
|
|
||||||
val defaults = defaultPaneLayout()
|
|
||||||
val order = preferences[orderKey]
|
|
||||||
?.split(',')
|
|
||||||
?.mapNotNull { value -> PaneId.entries.firstOrNull { it.name == value } }
|
|
||||||
?.let { parsed ->
|
|
||||||
val missing = PaneId.entries.filterNot { it in parsed }
|
|
||||||
parsed + missing
|
|
||||||
}
|
|
||||||
?.takeIf { it.isNotEmpty() }
|
|
||||||
?: defaults.order
|
|
||||||
|
|
||||||
val collapsed = preferences[collapsedKey]
|
|
||||||
?.split(',')
|
|
||||||
?.mapNotNull { value -> PaneId.entries.firstOrNull { it.name == value } }
|
|
||||||
?.toSet()
|
|
||||||
?: emptySet()
|
|
||||||
|
|
||||||
val parsedWeights = preferences[weightsKey]
|
|
||||||
?.split(';')
|
|
||||||
?.mapNotNull { item ->
|
|
||||||
val parts = item.split(':', limit = 2)
|
|
||||||
val paneId = PaneId.entries.firstOrNull { it.name == parts.getOrNull(0) }
|
|
||||||
val weight = parts.getOrNull(1)?.toFloatOrNull()
|
|
||||||
if (paneId != null && weight != null) paneId to weight.coerceAtLeast(0.6f) else null
|
|
||||||
}
|
|
||||||
?.toMap()
|
|
||||||
.orEmpty()
|
|
||||||
|
|
||||||
PaneLayout(
|
|
||||||
order = order,
|
|
||||||
collapsed = collapsed,
|
|
||||||
weights = PaneId.entries.associateWith { pane -> parsedWeights[pane] ?: defaults.weights.getValue(pane) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun save(layout: PaneLayout) {
|
|
||||||
dataStore.edit { preferences ->
|
|
||||||
preferences[orderKey] = layout.order.joinToString(",") { it.name }
|
|
||||||
preferences[collapsedKey] = layout.collapsed.joinToString(",") { it.name }
|
|
||||||
preferences[weightsKey] = layout.order.joinToString(";") { pane ->
|
|
||||||
"${pane.name}:${layout.weights[pane] ?: 1f}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun GitHugApp() {
|
fun GitHugApp() {
|
||||||
@@ -1174,94 +1050,3 @@ private fun TerminalKeyButton(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun movePane(layout: PaneLayout, paneId: PaneId, delta: Int): PaneLayout {
|
|
||||||
val currentIndex = layout.order.indexOf(paneId)
|
|
||||||
if (currentIndex == -1) return layout
|
|
||||||
val targetIndex = (currentIndex + delta).coerceIn(0, layout.order.lastIndex)
|
|
||||||
if (currentIndex == targetIndex) return layout
|
|
||||||
|
|
||||||
val updatedOrder = layout.order.toMutableList()
|
|
||||||
updatedOrder.removeAt(currentIndex)
|
|
||||||
updatedOrder.add(targetIndex, paneId)
|
|
||||||
return layout.copy(order = updatedOrder)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun resizePanes(
|
|
||||||
layout: PaneLayout,
|
|
||||||
upper: PaneId,
|
|
||||||
lower: PaneId,
|
|
||||||
dragFraction: Float,
|
|
||||||
): PaneLayout {
|
|
||||||
if (upper in layout.collapsed || lower in layout.collapsed) return layout
|
|
||||||
|
|
||||||
val minWeight = 0.6f
|
|
||||||
val upperWeight = layout.weights[upper] ?: 1f
|
|
||||||
val lowerWeight = layout.weights[lower] ?: 1f
|
|
||||||
val scaledDelta = dragFraction.coerceIn(-0.75f, 0.75f)
|
|
||||||
|
|
||||||
val newUpper = (upperWeight + scaledDelta).coerceAtLeast(minWeight)
|
|
||||||
val newLower = (lowerWeight - scaledDelta).coerceAtLeast(minWeight)
|
|
||||||
|
|
||||||
return layout.copy(
|
|
||||||
weights = layout.weights + mapOf(
|
|
||||||
upper to newUpper,
|
|
||||||
lower to newLower,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun recommendedPaneHeights(
|
|
||||||
level: Level,
|
|
||||||
levelCount: Int,
|
|
||||||
outputLineCount: Int,
|
|
||||||
screenHeightDp: Int,
|
|
||||||
suggestionsVisible: Boolean,
|
|
||||||
visibleHint: String?,
|
|
||||||
): Map<PaneId, Int> {
|
|
||||||
val screenHeight = screenHeightDp.coerceAtLeast(560)
|
|
||||||
val levelsHeight = (56 + levelCount * 44).coerceAtLeast(160)
|
|
||||||
val descriptionLines = (level.description.length / 42).coerceAtLeast(2) + 2
|
|
||||||
val suggestionsLines = if (suggestionsVisible) level.commandSuggestions.size + 1 else 0
|
|
||||||
val hintLines = visibleHint?.let { (it.length / 42).coerceAtLeast(1) + 1 } ?: 0
|
|
||||||
val exerciseHeight = (120 + descriptionLines * 24 + suggestionsLines * 22 + hintLines * 22).coerceAtLeast(180)
|
|
||||||
val terminalHeight = (170 + outputLineCount.coerceAtLeast(1) * 20).coerceAtMost((screenHeight * 0.7f).toInt())
|
|
||||||
val visualHeight = (screenHeight * 0.24f).toInt().coerceAtLeast(170)
|
|
||||||
|
|
||||||
return mapOf(
|
|
||||||
PaneId.EXERCISE to exerciseHeight,
|
|
||||||
PaneId.TERMINAL to terminalHeight,
|
|
||||||
PaneId.VISUAL to visualHeight,
|
|
||||||
PaneId.LEVELS to levelsHeight,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun recommendedPaneWeights(
|
|
||||||
heights: Map<PaneId, Int>,
|
|
||||||
): Map<PaneId, Float> {
|
|
||||||
fun weightFor(height: Int): Float = (height / 180f).coerceAtLeast(0.6f)
|
|
||||||
|
|
||||||
return heights.mapValues { (_, height) -> weightFor(height) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun recommendedWorkspaceHeight(
|
|
||||||
paneLayout: PaneLayout,
|
|
||||||
recommendedHeights: Map<PaneId, Int>,
|
|
||||||
): androidx.compose.ui.unit.Dp {
|
|
||||||
val dividerHeight = 18 * (paneLayout.order.size - 1)
|
|
||||||
val collapsedPaneHeight = 44 * paneLayout.collapsed.size
|
|
||||||
val expandedHeight = paneLayout.order
|
|
||||||
.filterNot { it in paneLayout.collapsed }
|
|
||||||
.sumOf { pane -> recommendedHeights[pane] ?: 0 }
|
|
||||||
return (expandedHeight + dividerHeight + collapsedPaneHeight).dp
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun commonPrefix(values: List<String>): String {
|
|
||||||
if (values.isEmpty()) return ""
|
|
||||||
var prefix = values.first()
|
|
||||||
values.drop(1).forEach { value ->
|
|
||||||
while (!value.startsWith(prefix) && prefix.isNotEmpty()) {
|
|
||||||
prefix = prefix.dropLast(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return prefix
|
|
||||||
}
|
|
||||||
|
|||||||
29
app/src/main/java/com/kawomi/githugandroid/GitHugTheme.kt
Normal file
29
app/src/main/java/com/kawomi/githugandroid/GitHugTheme.kt
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package com.kawomi.githugandroid
|
||||||
|
|
||||||
|
import androidx.compose.material3.darkColorScheme
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
|
val AppBackground = Color(0xFF000000)
|
||||||
|
val PanelPrimary = Color(0xFF121212)
|
||||||
|
val PanelSecondary = Color(0xFF1E1E1E)
|
||||||
|
val PanelTertiary = Color(0xFF262626)
|
||||||
|
val TerminalBackground = Color(0xFF050505)
|
||||||
|
val TextPrimary = Color(0xFFFFFFFF)
|
||||||
|
val TextSecondary = Color(0xFFE6E6E6)
|
||||||
|
val TextMuted = Color(0xFFBDBDBD)
|
||||||
|
val Accent = Color(0xFF00E5FF)
|
||||||
|
val Success = Color(0xFF00FF95)
|
||||||
|
|
||||||
|
val GitHugColorScheme = darkColorScheme(
|
||||||
|
primary = Accent,
|
||||||
|
onPrimary = Color.Black,
|
||||||
|
secondary = TextPrimary,
|
||||||
|
onSecondary = Color.Black,
|
||||||
|
background = AppBackground,
|
||||||
|
onBackground = TextPrimary,
|
||||||
|
surface = PanelPrimary,
|
||||||
|
onSurface = TextPrimary,
|
||||||
|
surfaceVariant = PanelSecondary,
|
||||||
|
onSurfaceVariant = TextSecondary,
|
||||||
|
outline = TextMuted,
|
||||||
|
)
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package com.kawomi.githugandroid
|
||||||
|
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
fun movePane(layout: PaneLayout, paneId: PaneId, delta: Int): PaneLayout {
|
||||||
|
val currentIndex = layout.order.indexOf(paneId)
|
||||||
|
if (currentIndex == -1) return layout
|
||||||
|
val targetIndex = (currentIndex + delta).coerceIn(0, layout.order.lastIndex)
|
||||||
|
if (currentIndex == targetIndex) return layout
|
||||||
|
|
||||||
|
val updatedOrder = layout.order.toMutableList()
|
||||||
|
updatedOrder.removeAt(currentIndex)
|
||||||
|
updatedOrder.add(targetIndex, paneId)
|
||||||
|
return layout.copy(order = updatedOrder)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resizePanes(
|
||||||
|
layout: PaneLayout,
|
||||||
|
upper: PaneId,
|
||||||
|
lower: PaneId,
|
||||||
|
dragFraction: Float,
|
||||||
|
): PaneLayout {
|
||||||
|
if (upper in layout.collapsed || lower in layout.collapsed) return layout
|
||||||
|
|
||||||
|
val minWeight = 0.6f
|
||||||
|
val upperWeight = layout.weights[upper] ?: 1f
|
||||||
|
val lowerWeight = layout.weights[lower] ?: 1f
|
||||||
|
val scaledDelta = dragFraction.coerceIn(-0.75f, 0.75f)
|
||||||
|
|
||||||
|
val newUpper = (upperWeight + scaledDelta).coerceAtLeast(minWeight)
|
||||||
|
val newLower = (lowerWeight - scaledDelta).coerceAtLeast(minWeight)
|
||||||
|
|
||||||
|
return layout.copy(
|
||||||
|
weights = layout.weights + mapOf(
|
||||||
|
upper to newUpper,
|
||||||
|
lower to newLower,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun recommendedPaneHeights(
|
||||||
|
level: Level,
|
||||||
|
levelCount: Int,
|
||||||
|
outputLineCount: Int,
|
||||||
|
screenHeightDp: Int,
|
||||||
|
suggestionsVisible: Boolean,
|
||||||
|
visibleHint: String?,
|
||||||
|
): Map<PaneId, Int> {
|
||||||
|
val screenHeight = screenHeightDp.coerceAtLeast(560)
|
||||||
|
val levelsHeight = (56 + levelCount * 44).coerceAtLeast(160)
|
||||||
|
val descriptionLines = (level.description.length / 42).coerceAtLeast(2) + 2
|
||||||
|
val suggestionsLines = if (suggestionsVisible) level.commandSuggestions.size + 1 else 0
|
||||||
|
val hintLines = visibleHint?.let { (it.length / 42).coerceAtLeast(1) + 1 } ?: 0
|
||||||
|
val exerciseHeight = (120 + descriptionLines * 24 + suggestionsLines * 22 + hintLines * 22).coerceAtLeast(180)
|
||||||
|
val terminalHeight = (170 + outputLineCount.coerceAtLeast(1) * 20).coerceAtMost((screenHeight * 0.7f).toInt())
|
||||||
|
val visualHeight = (screenHeight * 0.24f).toInt().coerceAtLeast(170)
|
||||||
|
|
||||||
|
return mapOf(
|
||||||
|
PaneId.EXERCISE to exerciseHeight,
|
||||||
|
PaneId.TERMINAL to terminalHeight,
|
||||||
|
PaneId.VISUAL to visualHeight,
|
||||||
|
PaneId.LEVELS to levelsHeight,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun recommendedPaneWeights(
|
||||||
|
heights: Map<PaneId, Int>,
|
||||||
|
): Map<PaneId, Float> {
|
||||||
|
fun weightFor(height: Int): Float = (height / 180f).coerceAtLeast(0.6f)
|
||||||
|
|
||||||
|
return heights.mapValues { (_, height) -> weightFor(height) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun recommendedWorkspaceHeight(
|
||||||
|
paneLayout: PaneLayout,
|
||||||
|
recommendedHeights: Map<PaneId, Int>,
|
||||||
|
): Dp {
|
||||||
|
val dividerHeight = 18 * (paneLayout.order.size - 1)
|
||||||
|
val collapsedPaneHeight = 44 * paneLayout.collapsed.size
|
||||||
|
val expandedHeight = paneLayout.order
|
||||||
|
.filterNot { it in paneLayout.collapsed }
|
||||||
|
.sumOf { pane -> recommendedHeights[pane] ?: 0 }
|
||||||
|
return (expandedHeight + dividerHeight + collapsedPaneHeight).dp
|
||||||
|
}
|
||||||
|
|
||||||
|
fun commonPrefix(values: List<String>): String {
|
||||||
|
if (values.isEmpty()) return ""
|
||||||
|
var prefix = values.first()
|
||||||
|
values.drop(1).forEach { value ->
|
||||||
|
while (!value.startsWith(prefix) && prefix.isNotEmpty()) {
|
||||||
|
prefix = prefix.dropLast(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prefix
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.kawomi.githugandroid
|
||||||
|
|
||||||
|
enum class PaneId(val title: String) {
|
||||||
|
LEVELS("Levels"),
|
||||||
|
VISUAL("Visual"),
|
||||||
|
EXERCISE("Exercise"),
|
||||||
|
TERMINAL("Terminal"),
|
||||||
|
}
|
||||||
|
|
||||||
|
data class PaneLayout(
|
||||||
|
val order: List<PaneId>,
|
||||||
|
val collapsed: Set<PaneId>,
|
||||||
|
val weights: Map<PaneId, Float>,
|
||||||
|
)
|
||||||
|
|
||||||
|
enum class ExerciseDetailPanel {
|
||||||
|
HINT,
|
||||||
|
SUGGESTIONS,
|
||||||
|
}
|
||||||
|
|
||||||
|
fun defaultPaneLayout(): PaneLayout = PaneLayout(
|
||||||
|
order = listOf(PaneId.EXERCISE, PaneId.TERMINAL, PaneId.VISUAL, PaneId.LEVELS),
|
||||||
|
collapsed = emptySet(),
|
||||||
|
weights = mapOf(
|
||||||
|
PaneId.EXERCISE to 0.95f,
|
||||||
|
PaneId.TERMINAL to 1.45f,
|
||||||
|
PaneId.VISUAL to 1.0f,
|
||||||
|
PaneId.LEVELS to 0.8f,
|
||||||
|
),
|
||||||
|
)
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package com.kawomi.githugandroid
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
|
||||||
|
import androidx.datastore.preferences.core.edit
|
||||||
|
import androidx.datastore.preferences.core.emptyPreferences
|
||||||
|
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||||
|
import androidx.datastore.preferences.preferencesDataStoreFile
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
class PaneLayoutStore(private val context: Context) {
|
||||||
|
private val dataStore = PreferenceDataStoreFactory.create(
|
||||||
|
produceFile = { context.preferencesDataStoreFile("pane_layout_preferences") }
|
||||||
|
)
|
||||||
|
|
||||||
|
private val orderKey = stringPreferencesKey("pane_order")
|
||||||
|
private val collapsedKey = stringPreferencesKey("pane_collapsed")
|
||||||
|
private val weightsKey = stringPreferencesKey("pane_weights")
|
||||||
|
|
||||||
|
val layoutFlow = dataStore.data
|
||||||
|
.catch { error ->
|
||||||
|
if (error is IOException) emit(emptyPreferences()) else throw error
|
||||||
|
}
|
||||||
|
.map { preferences ->
|
||||||
|
val defaults = defaultPaneLayout()
|
||||||
|
val order = preferences[orderKey]
|
||||||
|
?.split(',')
|
||||||
|
?.mapNotNull { value -> PaneId.entries.firstOrNull { it.name == value } }
|
||||||
|
?.let { parsed ->
|
||||||
|
val missing = PaneId.entries.filterNot { it in parsed }
|
||||||
|
parsed + missing
|
||||||
|
}
|
||||||
|
?.takeIf { it.isNotEmpty() }
|
||||||
|
?: defaults.order
|
||||||
|
|
||||||
|
val collapsed = preferences[collapsedKey]
|
||||||
|
?.split(',')
|
||||||
|
?.mapNotNull { value -> PaneId.entries.firstOrNull { it.name == value } }
|
||||||
|
?.toSet()
|
||||||
|
?: emptySet()
|
||||||
|
|
||||||
|
val parsedWeights = preferences[weightsKey]
|
||||||
|
?.split(';')
|
||||||
|
?.mapNotNull { item ->
|
||||||
|
val parts = item.split(':', limit = 2)
|
||||||
|
val paneId = PaneId.entries.firstOrNull { it.name == parts.getOrNull(0) }
|
||||||
|
val weight = parts.getOrNull(1)?.toFloatOrNull()
|
||||||
|
if (paneId != null && weight != null) paneId to weight.coerceAtLeast(0.6f) else null
|
||||||
|
}
|
||||||
|
?.toMap()
|
||||||
|
.orEmpty()
|
||||||
|
|
||||||
|
PaneLayout(
|
||||||
|
order = order,
|
||||||
|
collapsed = collapsed,
|
||||||
|
weights = PaneId.entries.associateWith { pane -> parsedWeights[pane] ?: defaults.weights.getValue(pane) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun save(layout: PaneLayout) {
|
||||||
|
dataStore.edit { preferences ->
|
||||||
|
preferences[orderKey] = layout.order.joinToString(",") { it.name }
|
||||||
|
preferences[collapsedKey] = layout.collapsed.joinToString(",") { it.name }
|
||||||
|
preferences[weightsKey] = layout.order.joinToString(";") { pane ->
|
||||||
|
"${pane.name}:${layout.weights[pane] ?: 1f}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user