perf: 优化字体选择器性能 (#1497)
This commit is contained in:
@@ -12,7 +12,6 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.data.repository.ReadPreferences
|
|
||||||
import io.legado.app.data.repository.ReadSettingsRepository
|
import io.legado.app.data.repository.ReadSettingsRepository
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
import io.legado.app.ui.book.read.sheet.BgTextConfigSheet
|
import io.legado.app.ui.book.read.sheet.BgTextConfigSheet
|
||||||
@@ -37,6 +36,7 @@ import io.legado.app.ui.book.read.sheet.SpeakEngineConfigSheet
|
|||||||
import io.legado.app.ui.book.read.sheet.TitleBarIconSheet
|
import io.legado.app.ui.book.read.sheet.TitleBarIconSheet
|
||||||
import io.legado.app.ui.book.read.sheet.ToolButtonConfigSheet
|
import io.legado.app.ui.book.read.sheet.ToolButtonConfigSheet
|
||||||
import io.legado.app.ui.book.read.sheet.UnderlineConfigSheet
|
import io.legado.app.ui.book.read.sheet.UnderlineConfigSheet
|
||||||
|
import io.legado.app.ui.widget.components.FontFolderState
|
||||||
import io.legado.app.ui.widget.components.FontSelectSheet
|
import io.legado.app.ui.widget.components.FontSelectSheet
|
||||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||||
import io.legado.app.ui.widget.components.changeSource.ChangeSourceSheet
|
import io.legado.app.ui.widget.components.changeSource.ChangeSourceSheet
|
||||||
@@ -172,15 +172,21 @@ fun ReadBookScreen(
|
|||||||
)
|
)
|
||||||
val fontSelectReadSettings: ReadSettingsRepository = org.koin.compose.koinInject()
|
val fontSelectReadSettings: ReadSettingsRepository = org.koin.compose.koinInject()
|
||||||
val fontSelectPreferences by fontSelectReadSettings.preferences.collectAsStateWithLifecycle(
|
val fontSelectPreferences by fontSelectReadSettings.preferences.collectAsStateWithLifecycle(
|
||||||
initialValue = ReadPreferences()
|
initialValue = null
|
||||||
)
|
)
|
||||||
val fontSelectFolderUri = fontSelectPreferences.fontFolder
|
val fontSelectFolderState = remember(fontSelectPreferences) {
|
||||||
.takeIf { it.isNotEmpty() }?.toUri()
|
val pref = fontSelectPreferences
|
||||||
|
if (pref == null) {
|
||||||
|
FontFolderState.Loading
|
||||||
|
} else {
|
||||||
|
FontFolderState.Loaded(pref.fontFolder.takeIf { it.isNotEmpty() }?.toUri())
|
||||||
|
}
|
||||||
|
}
|
||||||
val fontSelectSystemTypefaces = stringArrayResource(R.array.system_typefaces)
|
val fontSelectSystemTypefaces = stringArrayResource(R.array.system_typefaces)
|
||||||
FontSelectSheet(
|
FontSelectSheet(
|
||||||
show = state.activeSheet is ReadBookSheet.FontSelect,
|
show = state.activeSheet is ReadBookSheet.FontSelect,
|
||||||
title = stringResource(R.string.select_font),
|
title = stringResource(R.string.select_font),
|
||||||
fontFolderUri = fontSelectFolderUri,
|
folderState = fontSelectFolderState,
|
||||||
selectedFontPath = ReadBookConfig.textFont,
|
selectedFontPath = ReadBookConfig.textFont,
|
||||||
onDismissRequest = dismissSheet,
|
onDismissRequest = dismissSheet,
|
||||||
onSelectFont = { onIntent(ReadBookIntent.SelectFont(it.uri.toString())) },
|
onSelectFont = { onIntent(ReadBookIntent.SelectFont(it.uri.toString())) },
|
||||||
@@ -191,7 +197,7 @@ fun ReadBookScreen(
|
|||||||
FontSelectSheet(
|
FontSelectSheet(
|
||||||
show = state.activeSheet is ReadBookSheet.TitleFontSelect,
|
show = state.activeSheet is ReadBookSheet.TitleFontSelect,
|
||||||
title = stringResource(R.string.read_config_title_settings),
|
title = stringResource(R.string.read_config_title_settings),
|
||||||
fontFolderUri = fontSelectFolderUri,
|
folderState = fontSelectFolderState,
|
||||||
selectedFontPath = ReadBookConfig.titleFont,
|
selectedFontPath = ReadBookConfig.titleFont,
|
||||||
onDismissRequest = dismissSheet,
|
onDismissRequest = dismissSheet,
|
||||||
onSelectFont = { onIntent(ReadBookIntent.SelectTitleFont(it.uri.toString())) },
|
onSelectFont = { onIntent(ReadBookIntent.SelectTitleFont(it.uri.toString())) },
|
||||||
|
|||||||
@@ -49,12 +49,12 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.data.repository.ReadPreferences
|
|
||||||
import io.legado.app.data.repository.ReadSettingsRepository
|
import io.legado.app.data.repository.ReadSettingsRepository
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
import io.legado.app.ui.book.read.ConfigUpdate
|
import io.legado.app.ui.book.read.ConfigUpdate
|
||||||
import io.legado.app.ui.book.read.ReadBookIntent
|
import io.legado.app.ui.book.read.ReadBookIntent
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
import io.legado.app.ui.widget.components.FontFolderState
|
||||||
import io.legado.app.ui.widget.components.FontSelectSheet
|
import io.legado.app.ui.widget.components.FontSelectSheet
|
||||||
import io.legado.app.ui.widget.components.SectionTitle
|
import io.legado.app.ui.widget.components.SectionTitle
|
||||||
import io.legado.app.ui.widget.components.dialog.ColorPickerSheet
|
import io.legado.app.ui.widget.components.dialog.ColorPickerSheet
|
||||||
@@ -568,15 +568,22 @@ internal fun HeaderFooterPage(
|
|||||||
// Font selector for header/footer
|
// Font selector for header/footer
|
||||||
val readSettingsRepository: ReadSettingsRepository = koinInject()
|
val readSettingsRepository: ReadSettingsRepository = koinInject()
|
||||||
val preferences by readSettingsRepository.preferences.collectAsStateWithLifecycle(
|
val preferences by readSettingsRepository.preferences.collectAsStateWithLifecycle(
|
||||||
initialValue = ReadPreferences()
|
initialValue = null
|
||||||
)
|
)
|
||||||
val fontFolderUri = preferences.fontFolder.takeIf { it.isNotEmpty() }?.toUri()
|
val fontFolderState = remember(preferences) {
|
||||||
|
val pref = preferences
|
||||||
|
if (pref == null) {
|
||||||
|
FontFolderState.Loading
|
||||||
|
} else {
|
||||||
|
FontFolderState.Loaded(pref.fontFolder.takeIf { it.isNotEmpty() }?.toUri())
|
||||||
|
}
|
||||||
|
}
|
||||||
val systemTypefaces = stringArrayResource(R.array.system_typefaces)
|
val systemTypefaces = stringArrayResource(R.array.system_typefaces)
|
||||||
|
|
||||||
FontSelectSheet(
|
FontSelectSheet(
|
||||||
show = showFontSelect,
|
show = showFontSelect,
|
||||||
title = stringResource(R.string.select_font),
|
title = stringResource(R.string.select_font),
|
||||||
fontFolderUri = fontFolderUri,
|
folderState = fontFolderState,
|
||||||
selectedFontPath = ReadBookConfig.headerFont,
|
selectedFontPath = ReadBookConfig.headerFont,
|
||||||
onDismissRequest = { showFontSelect = false },
|
onDismissRequest = { showFontSelect = false },
|
||||||
onSelectFont = {
|
onSelectFont = {
|
||||||
|
|||||||
@@ -43,13 +43,13 @@ import androidx.core.net.toUri
|
|||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.data.entities.HighlightRule
|
import io.legado.app.data.entities.HighlightRule
|
||||||
import io.legado.app.data.repository.ReadPreferences
|
|
||||||
import io.legado.app.data.repository.ReadSettingsRepository
|
import io.legado.app.data.repository.ReadSettingsRepository
|
||||||
import io.legado.app.data.repository.configNames
|
import io.legado.app.data.repository.configNames
|
||||||
import io.legado.app.data.repository.toJsonArray
|
import io.legado.app.data.repository.toJsonArray
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
import io.legado.app.ui.widget.components.AppTextField
|
import io.legado.app.ui.widget.components.AppTextField
|
||||||
|
import io.legado.app.ui.widget.components.FontFolderState
|
||||||
import io.legado.app.ui.widget.components.FontSelectSheet
|
import io.legado.app.ui.widget.components.FontSelectSheet
|
||||||
import io.legado.app.ui.widget.components.SectionTitle
|
import io.legado.app.ui.widget.components.SectionTitle
|
||||||
import io.legado.app.ui.widget.components.card.NormalCard
|
import io.legado.app.ui.widget.components.card.NormalCard
|
||||||
@@ -515,9 +515,16 @@ fun HighlightRuleEditSheet(
|
|||||||
val readSettingsRepository: ReadSettingsRepository = org.koin.compose.koinInject()
|
val readSettingsRepository: ReadSettingsRepository = org.koin.compose.koinInject()
|
||||||
val fontSelectScope = rememberCoroutineScope()
|
val fontSelectScope = rememberCoroutineScope()
|
||||||
val fontSelectPreferences by readSettingsRepository.preferences.collectAsStateWithLifecycle(
|
val fontSelectPreferences by readSettingsRepository.preferences.collectAsStateWithLifecycle(
|
||||||
initialValue = ReadPreferences()
|
initialValue = null
|
||||||
)
|
)
|
||||||
val fontFolderUri = fontSelectPreferences.fontFolder.takeIf { it.isNotEmpty() }?.toUri()
|
val fontFolderState = remember(fontSelectPreferences) {
|
||||||
|
val pref = fontSelectPreferences
|
||||||
|
if (pref == null) {
|
||||||
|
FontFolderState.Loading
|
||||||
|
} else {
|
||||||
|
FontFolderState.Loaded(pref.fontFolder.takeIf { it.isNotEmpty() }?.toUri())
|
||||||
|
}
|
||||||
|
}
|
||||||
val systemTypefaces = stringArrayResource(R.array.system_typefaces)
|
val systemTypefaces = stringArrayResource(R.array.system_typefaces)
|
||||||
val fontFolderLauncher = rememberLauncherForActivityResult(
|
val fontFolderLauncher = rememberLauncherForActivityResult(
|
||||||
ActivityResultContracts.OpenDocumentTree()
|
ActivityResultContracts.OpenDocumentTree()
|
||||||
@@ -534,7 +541,7 @@ fun HighlightRuleEditSheet(
|
|||||||
FontSelectSheet(
|
FontSelectSheet(
|
||||||
show = showFontSelect,
|
show = showFontSelect,
|
||||||
title = stringResource(R.string.select_font),
|
title = stringResource(R.string.select_font),
|
||||||
fontFolderUri = fontFolderUri,
|
folderState = fontFolderState,
|
||||||
selectedFontPath = fontPath,
|
selectedFontPath = fontPath,
|
||||||
onDismissRequest = { showFontSelect = false },
|
onDismissRequest = { showFontSelect = false },
|
||||||
onSelectFont = { fontPath = it.uri.toString(); showFontSelect = false },
|
onSelectFont = { fontPath = it.uri.toString(); showFontSelect = false },
|
||||||
|
|||||||
@@ -74,7 +74,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.base.AppContextWrapper
|
import io.legado.app.base.AppContextWrapper
|
||||||
import io.legado.app.constant.EventBus
|
import io.legado.app.constant.EventBus
|
||||||
import io.legado.app.constant.PreferKey
|
|
||||||
import io.legado.app.help.LauncherIconHelp
|
import io.legado.app.help.LauncherIconHelp
|
||||||
import io.legado.app.help.config.ThemeConfigStore
|
import io.legado.app.help.config.ThemeConfigStore
|
||||||
import io.legado.app.ui.config.labConfig.LabConfig
|
import io.legado.app.ui.config.labConfig.LabConfig
|
||||||
@@ -83,6 +82,7 @@ import io.legado.app.ui.theme.ThemeEngine
|
|||||||
import io.legado.app.ui.theme.ThemeResolver
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
import io.legado.app.ui.theme.adaptiveContentPadding
|
import io.legado.app.ui.theme.adaptiveContentPadding
|
||||||
import io.legado.app.ui.widget.components.AppScaffold
|
import io.legado.app.ui.widget.components.AppScaffold
|
||||||
|
import io.legado.app.ui.widget.components.FontFolderState
|
||||||
import io.legado.app.ui.widget.components.FontSelectSheet
|
import io.legado.app.ui.widget.components.FontSelectSheet
|
||||||
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
||||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||||
@@ -98,9 +98,7 @@ import io.legado.app.ui.widget.components.text.AppText
|
|||||||
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
|
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
||||||
import io.legado.app.ui.widget.components.topbar.TopBarNavigationButton
|
import io.legado.app.ui.widget.components.topbar.TopBarNavigationButton
|
||||||
import io.legado.app.utils.getPrefString
|
|
||||||
import io.legado.app.utils.postEvent
|
import io.legado.app.utils.postEvent
|
||||||
import io.legado.app.utils.putPrefString
|
|
||||||
import io.legado.app.utils.restart
|
import io.legado.app.utils.restart
|
||||||
import io.legado.app.utils.takePersistablePermissionSafely
|
import io.legado.app.utils.takePersistablePermissionSafely
|
||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
@@ -128,19 +126,22 @@ fun ThemeConfigScreen(
|
|||||||
var showFontSheet by remember { mutableStateOf(false) }
|
var showFontSheet by remember { mutableStateOf(false) }
|
||||||
val showThemeRefactorTip by viewModel.showThemeRefactorTip.collectAsStateWithLifecycle()
|
val showThemeRefactorTip by viewModel.showThemeRefactorTip.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
var fontFolderUri by remember {
|
val fontFolder by viewModel.fontFolder.collectAsStateWithLifecycle()
|
||||||
mutableStateOf(
|
val fontFolderState = remember(fontFolder) {
|
||||||
context.getPrefString(PreferKey.fontFolder)?.toUri()
|
val folder = fontFolder
|
||||||
)
|
if (folder == null) {
|
||||||
|
FontFolderState.Loading
|
||||||
|
} else {
|
||||||
|
FontFolderState.Loaded(folder.takeIf { it.isNotEmpty() }?.toUri())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val fontFolderLauncher = rememberLauncherForActivityResult(
|
val fontFolderLauncher = rememberLauncherForActivityResult(
|
||||||
ActivityResultContracts.OpenDocumentTree()
|
ActivityResultContracts.OpenDocumentTree()
|
||||||
) { uri ->
|
) { uri ->
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
fontFolderUri = uri
|
|
||||||
uri.takePersistablePermissionSafely(context, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
uri.takePersistablePermissionSafely(context, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
context.putPrefString(PreferKey.fontFolder, uri.toString())
|
viewModel.setFontFolder(uri.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -869,7 +870,7 @@ fun ThemeConfigScreen(
|
|||||||
FontSelectSheet(
|
FontSelectSheet(
|
||||||
show = showFontSheet,
|
show = showFontSheet,
|
||||||
title = stringResource(R.string.font_setting),
|
title = stringResource(R.string.font_setting),
|
||||||
fontFolderUri = fontFolderUri,
|
folderState = fontFolderState,
|
||||||
selectedFontPath = ThemeConfig.appFontPath,
|
selectedFontPath = ThemeConfig.appFontPath,
|
||||||
onDismissRequest = { showFontSheet = false },
|
onDismissRequest = { showFontSheet = false },
|
||||||
onSelectFont = { doc ->
|
onSelectFont = { doc ->
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import androidx.lifecycle.viewModelScope
|
|||||||
import io.legado.app.constant.PreferKey
|
import io.legado.app.constant.PreferKey
|
||||||
import io.legado.app.data.local.preferences.LocalPreferencesKeys
|
import io.legado.app.data.local.preferences.LocalPreferencesKeys
|
||||||
import io.legado.app.data.local.preferences.LocalPreferencesRepository
|
import io.legado.app.data.local.preferences.LocalPreferencesRepository
|
||||||
|
import io.legado.app.data.repository.ReadSettingsRepository
|
||||||
import io.legado.app.utils.FileDoc
|
import io.legado.app.utils.FileDoc
|
||||||
import io.legado.app.utils.FileUtils
|
import io.legado.app.utils.FileUtils
|
||||||
import io.legado.app.utils.MD5Utils
|
import io.legado.app.utils.MD5Utils
|
||||||
@@ -13,6 +14,7 @@ import io.legado.app.utils.externalFiles
|
|||||||
import io.legado.app.utils.inputStream
|
import io.legado.app.utils.inputStream
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -21,9 +23,22 @@ import java.io.File
|
|||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
|
|
||||||
class ThemeConfigViewModel(
|
class ThemeConfigViewModel(
|
||||||
private val localPreferencesRepository: LocalPreferencesRepository
|
private val localPreferencesRepository: LocalPreferencesRepository,
|
||||||
|
private val readSettingsRepository: ReadSettingsRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
val fontFolder = readSettingsRepository.preferences
|
||||||
|
.map { it.fontFolder as String? }
|
||||||
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)
|
||||||
|
|
||||||
|
fun setFontFolder(path: String) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
runCatching {
|
||||||
|
readSettingsRepository.setFontFolder(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val showThemeRefactorTip = localPreferencesRepository
|
val showThemeRefactorTip = localPreferencesRepository
|
||||||
.getPreference(LocalPreferencesKeys.SHOW_THEME_REFACTOR_TIP, true)
|
.getPreference(LocalPreferencesKeys.SHOW_THEME_REFACTOR_TIP, true)
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), true)
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), true)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package io.legado.app.ui.widget.components
|
package io.legado.app.ui.widget.components
|
||||||
|
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
|
import android.net.Uri
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@@ -25,8 +26,10 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.produceState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -41,35 +44,49 @@ import io.legado.app.R
|
|||||||
import io.legado.app.help.loadFontFiles
|
import io.legado.app.help.loadFontFiles
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
import io.legado.app.utils.FileDoc
|
import io.legado.app.utils.FileDoc
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
private val fontGridHeight = 360.dp
|
||||||
|
|
||||||
|
|
||||||
|
sealed interface FontFolderState {
|
||||||
|
data object Loading : FontFolderState
|
||||||
|
data class Loaded(val uri: Uri?) : FontFolderState
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shared font selection grid with search support.
|
* Shared font selection grid with search support.
|
||||||
*
|
*
|
||||||
* @param fontFolderUri URI of the font folder to load from
|
* @param folderState 字体文件夹加载状态,见 [FontFolderState]
|
||||||
* @param selectedFontName currently selected font name (for check mark), null to hide
|
* @param selectedFontName currently selected font name (for check mark), null to hide
|
||||||
* @param onSelectFont called when a font file is selected
|
* @param onSelectFont called when a font file is selected
|
||||||
* @param emptyText text to show when no fonts found
|
* @param emptyText text to show when no fonts found
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun FontSelectGrid(
|
fun FontSelectGrid(
|
||||||
fontFolderUri: android.net.Uri?,
|
folderState: FontFolderState,
|
||||||
selectedFontName: String?,
|
selectedFontName: String?,
|
||||||
onSelectFont: (FileDoc) -> Unit,
|
onSelectFont: (FileDoc) -> Unit,
|
||||||
emptyText: String? = null,
|
emptyText: String? = null,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
var fontItems by remember { mutableStateOf<List<FileDoc>>(emptyList()) }
|
var fontItems by remember { mutableStateOf<List<FileDoc>>(emptyList()) }
|
||||||
var isLoading by remember { mutableStateOf(true) }
|
var filesLoading by remember { mutableStateOf(false) }
|
||||||
var searchQuery by remember { mutableStateOf("") }
|
var searchQuery by remember { mutableStateOf("") }
|
||||||
|
|
||||||
LaunchedEffect(fontFolderUri) {
|
LaunchedEffect(folderState) {
|
||||||
isLoading = true
|
if (folderState is FontFolderState.Loaded) {
|
||||||
fontItems = withContext(Dispatchers.IO) {
|
filesLoading = true
|
||||||
loadFontFiles(context, fontFolderUri)
|
try {
|
||||||
|
fontItems = withContext(Dispatchers.IO) {
|
||||||
|
loadFontFiles(context, folderState.uri)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
filesLoading = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
isLoading = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val filteredItems = remember(fontItems, searchQuery) {
|
val filteredItems = remember(fontItems, searchQuery) {
|
||||||
@@ -77,6 +94,8 @@ fun FontSelectGrid(
|
|||||||
else fontItems.filter { it.name.contains(searchQuery, ignoreCase = true) }
|
else fontItems.filter { it.name.contains(searchQuery, ignoreCase = true) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val showLoading = folderState is FontFolderState.Loading || filesLoading
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -92,11 +111,11 @@ fun FontSelectGrid(
|
|||||||
Spacer(Modifier.height(4.dp))
|
Spacer(Modifier.height(4.dp))
|
||||||
|
|
||||||
// Font grid
|
// Font grid
|
||||||
if (isLoading) {
|
if (showLoading) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(200.dp),
|
.height(fontGridHeight),
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
CircularProgressIndicator()
|
CircularProgressIndicator()
|
||||||
@@ -105,7 +124,7 @@ fun FontSelectGrid(
|
|||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(100.dp),
|
.height(fontGridHeight),
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
@@ -120,7 +139,7 @@ fun FontSelectGrid(
|
|||||||
contentPadding = PaddingValues(vertical = 4.dp),
|
contentPadding = PaddingValues(vertical = 4.dp),
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
modifier = Modifier.height(360.dp),
|
modifier = Modifier.height(fontGridHeight),
|
||||||
) {
|
) {
|
||||||
items(filteredItems, key = { it.name }) { item ->
|
items(filteredItems, key = { it.name }) { item ->
|
||||||
FontItem(
|
FontItem(
|
||||||
@@ -140,21 +159,7 @@ private fun FontItem(
|
|||||||
isSelected: Boolean,
|
isSelected: Boolean,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val fontFamily by rememberItemFontFamily(item.uri)
|
||||||
val fontFamily = remember(item.uri) {
|
|
||||||
runCatching {
|
|
||||||
val uri = item.uri
|
|
||||||
val typeface: Typeface? = if (uri.scheme == "content") {
|
|
||||||
context.contentResolver.openFileDescriptor(uri, "r")?.use {
|
|
||||||
Typeface.Builder(it.fileDescriptor).build()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
uri.path?.let { Typeface.createFromFile(it) }
|
|
||||||
}
|
|
||||||
typeface?.let { FontFamily(it) }
|
|
||||||
}.getOrNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -186,3 +191,23 @@ private fun FontItem(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun rememberItemFontFamily(uri: Uri): State<FontFamily?> {
|
||||||
|
val context = LocalContext.current
|
||||||
|
return produceState<FontFamily?>(initialValue = null, uri) {
|
||||||
|
val parsed = withContext(Dispatchers.IO) {
|
||||||
|
runCatching {
|
||||||
|
val typeface: Typeface? = if (uri.scheme == "content") {
|
||||||
|
context.contentResolver.openFileDescriptor(uri, "r")?.use {
|
||||||
|
Typeface.Builder(it.fileDescriptor).build()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uri.path?.let { Typeface.createFromFile(it) }
|
||||||
|
}
|
||||||
|
typeface?.let { FontFamily(it) }
|
||||||
|
}.onFailure { if (it is CancellationException) throw it }.getOrNull()
|
||||||
|
}
|
||||||
|
if (parsed != null) value = parsed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package io.legado.app.ui.widget.components
|
package io.legado.app.ui.widget.components
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.FolderOpen
|
import androidx.compose.material.icons.filled.FolderOpen
|
||||||
import androidx.compose.material.icons.filled.TextFields
|
import androidx.compose.material.icons.filled.TextFields
|
||||||
@@ -24,7 +23,7 @@ import io.legado.app.utils.FileDoc
|
|||||||
fun FontSelectSheet(
|
fun FontSelectSheet(
|
||||||
show: Boolean = true,
|
show: Boolean = true,
|
||||||
title: String,
|
title: String,
|
||||||
fontFolderUri: Uri?,
|
folderState: FontFolderState,
|
||||||
selectedFontPath: String?,
|
selectedFontPath: String?,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
onSelectFont: (FileDoc) -> Unit,
|
onSelectFont: (FileDoc) -> Unit,
|
||||||
@@ -83,7 +82,7 @@ fun FontSelectSheet(
|
|||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
FontSelectGrid(
|
FontSelectGrid(
|
||||||
fontFolderUri = fontFolderUri,
|
folderState = folderState,
|
||||||
selectedFontName = selectedFontName,
|
selectedFontName = selectedFontName,
|
||||||
onSelectFont = { doc ->
|
onSelectFont = { doc ->
|
||||||
onSelectFont(doc)
|
onSelectFont(doc)
|
||||||
|
|||||||
Reference in New Issue
Block a user