mirror of
https://github.com/fromchat-messenger/app.git
synced 2026-09-22 19:15:05 +03:00
Restyle plugins UI with Material 3 expressive tokens
Replace hardcoded exteraGram colors with theme-aware cards, SearchBar, SettingsDetailTopBar, ExpressiveIconFrame, and tonal action buttons. Co-authored-by: denis0001-dev <denis0001.dev@ya.ru>
This commit is contained in:
@@ -351,6 +351,7 @@
|
|||||||
<string name="plugins_enabled">Включено</string>
|
<string name="plugins_enabled">Включено</string>
|
||||||
<string name="plugins_settings_title">Настройки плагина</string>
|
<string name="plugins_settings_title">Настройки плагина</string>
|
||||||
<string name="plugins_search">Поиск плагинов</string>
|
<string name="plugins_search">Поиск плагинов</string>
|
||||||
|
<string name="plugins_add">Добавить плагин</string>
|
||||||
<string name="plugins_usage">Использование:</string>
|
<string name="plugins_usage">Использование:</string>
|
||||||
<string name="plugins_version_author">Версия %1$s • @%2$s</string>
|
<string name="plugins_version_author">Версия %1$s • @%2$s</string>
|
||||||
<string name="plugins_delete">Удалить</string>
|
<string name="plugins_delete">Удалить</string>
|
||||||
|
|||||||
@@ -382,6 +382,7 @@
|
|||||||
<string name="plugins_enabled">Enabled</string>
|
<string name="plugins_enabled">Enabled</string>
|
||||||
<string name="plugins_settings_title">Plugin settings</string>
|
<string name="plugins_settings_title">Plugin settings</string>
|
||||||
<string name="plugins_search">Search plugins</string>
|
<string name="plugins_search">Search plugins</string>
|
||||||
|
<string name="plugins_add">Add plugin</string>
|
||||||
<string name="plugins_usage">Usage:</string>
|
<string name="plugins_usage">Usage:</string>
|
||||||
<string name="plugins_version_author">Version %1$s • @%2$s</string>
|
<string name="plugins_version_author">Version %1$s • @%2$s</string>
|
||||||
<string name="plugins_delete">Delete</string>
|
<string name="plugins_delete">Delete</string>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package ru.fromchat.ui.main.settings
|
package ru.fromchat.ui.main.settings
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@@ -10,31 +10,30 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.OpenInNew
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
import androidx.compose.material.icons.filled.Extension
|
import androidx.compose.material.icons.filled.Extension
|
||||||
import androidx.compose.material.icons.filled.Info
|
import androidx.compose.material.icons.filled.Info
|
||||||
import androidx.compose.material.icons.filled.OpenInNew
|
|
||||||
import androidx.compose.material.icons.filled.PushPin
|
import androidx.compose.material.icons.filled.PushPin
|
||||||
import androidx.compose.material.icons.filled.Search
|
|
||||||
import androidx.compose.material.icons.filled.Share
|
import androidx.compose.material.icons.filled.Share
|
||||||
import androidx.compose.material.icons.outlined.Add
|
import androidx.compose.material.icons.outlined.Add
|
||||||
import androidx.compose.material.icons.outlined.PushPin
|
import androidx.compose.material.icons.outlined.PushPin
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
|
import androidx.compose.material3.FilledTonalIconButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.IconButtonDefaults
|
||||||
|
import androidx.compose.material3.MaterialShapes
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.SwitchDefaults
|
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -44,7 +43,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.pr0gramm3r101.components.Category
|
import com.pr0gramm3r101.components.Category
|
||||||
@@ -52,8 +51,8 @@ import com.pr0gramm3r101.components.ListItem
|
|||||||
import com.pr0gramm3r101.components.SwitchListItem
|
import com.pr0gramm3r101.components.SwitchListItem
|
||||||
import com.pr0gramm3r101.utils.supportClipboardManagerImpl
|
import com.pr0gramm3r101.utils.supportClipboardManagerImpl
|
||||||
import com.pr0gramm3r101.utils.verticalScroll
|
import com.pr0gramm3r101.utils.verticalScroll
|
||||||
import org.jetbrains.compose.resources.stringResource
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.jetbrains.compose.resources.stringResource
|
||||||
import ru.fromchat.Res
|
import ru.fromchat.Res
|
||||||
import ru.fromchat.back
|
import ru.fromchat.back
|
||||||
import ru.fromchat.cancel
|
import ru.fromchat.cancel
|
||||||
@@ -62,7 +61,10 @@ import ru.fromchat.plugins.PluginSetting
|
|||||||
import ru.fromchat.plugins.host.PluginEngine
|
import ru.fromchat.plugins.host.PluginEngine
|
||||||
import ru.fromchat.plugins.integration.PluginInstallSupport
|
import ru.fromchat.plugins.integration.PluginInstallSupport
|
||||||
import ru.fromchat.ui.LocalNavController
|
import ru.fromchat.ui.LocalNavController
|
||||||
|
import ru.fromchat.ui.components.ExpressiveIconFrame
|
||||||
|
import ru.fromchat.ui.components.SearchBar
|
||||||
import ru.fromchat.ui.components.Text
|
import ru.fromchat.ui.components.Text
|
||||||
|
import ru.fromchat.plugins_add
|
||||||
import ru.fromchat.plugins_delete
|
import ru.fromchat.plugins_delete
|
||||||
import ru.fromchat.plugins_delete_confirm
|
import ru.fromchat.plugins_delete_confirm
|
||||||
import ru.fromchat.plugins_developer_mode
|
import ru.fromchat.plugins_developer_mode
|
||||||
@@ -80,16 +82,13 @@ import ru.fromchat.plugins_title
|
|||||||
import ru.fromchat.plugins_usage
|
import ru.fromchat.plugins_usage
|
||||||
import ru.fromchat.plugins_version_author
|
import ru.fromchat.plugins_version_author
|
||||||
|
|
||||||
private val PluginMasterBlue = Color(0xFF3D85C6)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
||||||
private val PluginCardBackground = Color(0xFF1A1A1A)
|
|
||||||
private val PluginDestructiveRed = Color(0xFFD9534F)
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PluginsScreen() {
|
fun PluginsScreen() {
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val clipboard = supportClipboardManagerImpl
|
val clipboard = supportClipboardManagerImpl
|
||||||
|
val scrollBehavior = rememberSettingsCollapsingScrollBehavior()
|
||||||
val engineEnabled by PluginEngine.engineEnabled.collectAsState()
|
val engineEnabled by PluginEngine.engineEnabled.collectAsState()
|
||||||
val developerMode by PluginEngine.developerMode.collectAsState()
|
val developerMode by PluginEngine.developerMode.collectAsState()
|
||||||
val installed by PluginEngine.installed.collectAsState()
|
val installed by PluginEngine.installed.collectAsState()
|
||||||
@@ -119,8 +118,15 @@ fun PluginsScreen() {
|
|||||||
title = { Text(stringResource(Res.string.plugins_info_title)) },
|
title = { Text(stringResource(Res.string.plugins_info_title)) },
|
||||||
text = {
|
text = {
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||||
Text(stringResource(Res.string.plugins_info_body))
|
Text(
|
||||||
Text(stringResource(Res.string.plugins_engine_d))
|
stringResource(Res.string.plugins_info_body),
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
stringResource(Res.string.plugins_engine_d),
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
)
|
||||||
SwitchListItem(
|
SwitchListItem(
|
||||||
headline = stringResource(Res.string.plugins_developer_mode),
|
headline = stringResource(Res.string.plugins_developer_mode),
|
||||||
supportingText = stringResource(Res.string.plugins_developer_mode_d),
|
supportingText = stringResource(Res.string.plugins_developer_mode_d),
|
||||||
@@ -150,7 +156,10 @@ fun PluginsScreen() {
|
|||||||
deleteTarget = null
|
deleteTarget = null
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
Text(stringResource(Res.string.plugins_delete))
|
Text(
|
||||||
|
stringResource(Res.string.plugins_delete),
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dismissButton = {
|
dismissButton = {
|
||||||
@@ -174,9 +183,12 @@ fun PluginsScreen() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
|
containerColor = MaterialTheme.colorScheme.background,
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
SettingsDetailTopBar(
|
||||||
title = {
|
title = {
|
||||||
Text(
|
Text(
|
||||||
stringResource(Res.string.plugins_title),
|
stringResource(Res.string.plugins_title),
|
||||||
@@ -184,31 +196,10 @@ fun PluginsScreen() {
|
|||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
navigationIcon = {
|
onBack = { navController.navigateUp() },
|
||||||
IconButton(onClick = { navController.navigateUp() }) {
|
scrollBehavior = scrollBehavior,
|
||||||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(Res.string.back))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions = {
|
|
||||||
IconButton(onClick = { /* search toggled via field below */ }) {
|
|
||||||
Icon(Icons.Default.Search, contentDescription = stringResource(Res.string.plugins_search))
|
|
||||||
}
|
|
||||||
IconButton(onClick = { PluginInstallSupport.installFromPicker() }) {
|
|
||||||
Icon(Icons.Outlined.Add, contentDescription = null)
|
|
||||||
}
|
|
||||||
IconButton(onClick = { showInfoDialog = true }) {
|
|
||||||
Icon(Icons.Default.Info, contentDescription = stringResource(Res.string.plugins_info_title))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
colors = TopAppBarDefaults.topAppBarColors(
|
|
||||||
containerColor = Color.Black,
|
|
||||||
titleContentColor = Color.White,
|
|
||||||
navigationIconContentColor = Color.White,
|
|
||||||
actionIconContentColor = Color.White,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
containerColor = Color.Black,
|
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
Column(
|
Column(
|
||||||
Modifier
|
Modifier
|
||||||
@@ -217,18 +208,38 @@ fun PluginsScreen() {
|
|||||||
.padding(innerPadding)
|
.padding(innerPadding)
|
||||||
.padding(horizontal = 16.dp),
|
.padding(horizontal = 16.dp),
|
||||||
) {
|
) {
|
||||||
OutlinedTextField(
|
SearchBar(
|
||||||
value = searchQuery,
|
query = searchQuery,
|
||||||
onValueChange = { searchQuery = it },
|
onQueryChange = { searchQuery = it },
|
||||||
|
onSearch = {},
|
||||||
|
placeholder = stringResource(Res.string.plugins_search),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 8.dp, bottom = 12.dp),
|
.padding(top = 8.dp, bottom = 16.dp),
|
||||||
placeholder = { Text(stringResource(Res.string.plugins_search)) },
|
|
||||||
leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
|
|
||||||
singleLine = true,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
PluginMasterToggle(
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = 8.dp),
|
||||||
|
horizontalArrangement = Arrangement.End,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
FilledTonalIconButton(onClick = { PluginInstallSupport.installFromPicker() }) {
|
||||||
|
Icon(
|
||||||
|
Icons.Outlined.Add,
|
||||||
|
contentDescription = stringResource(Res.string.plugins_add),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
IconButton(onClick = { showInfoDialog = true }) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Info,
|
||||||
|
contentDescription = stringResource(Res.string.plugins_info_title),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginEngineMasterCard(
|
||||||
checked = engineEnabled,
|
checked = engineEnabled,
|
||||||
onCheckedChange = PluginEngine::setEngineEnabled,
|
onCheckedChange = PluginEngine::setEngineEnabled,
|
||||||
)
|
)
|
||||||
@@ -242,7 +253,8 @@ fun PluginsScreen() {
|
|||||||
} else {
|
} else {
|
||||||
stringResource(Res.string.plugins_no_results)
|
stringResource(Res.string.plugins_no_results)
|
||||||
},
|
},
|
||||||
color = Color.White.copy(alpha = 0.7f),
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
modifier = Modifier.padding(vertical = 24.dp),
|
modifier = Modifier.padding(vertical = 24.dp),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@@ -277,43 +289,46 @@ fun PluginsScreen() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun PluginMasterToggle(
|
private fun PluginEngineMasterCard(
|
||||||
checked: Boolean,
|
checked: Boolean,
|
||||||
onCheckedChange: (Boolean) -> Unit,
|
onCheckedChange: (Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
Surface(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxWidth(),
|
||||||
.fillMaxWidth()
|
shape = MaterialTheme.shapes.extraLarge,
|
||||||
.clickable { onCheckedChange(!checked) },
|
colors = CardDefaults.cardColors(
|
||||||
shape = RoundedCornerShape(12.dp),
|
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
color = PluginMasterBlue,
|
),
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp, vertical = 14.dp),
|
.padding(horizontal = 20.dp, vertical = 16.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
) {
|
) {
|
||||||
Text(
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
text = stringResource(Res.string.plugins_enable_system),
|
Text(
|
||||||
color = Color.White,
|
text = stringResource(Res.string.plugins_enable_system),
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
)
|
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(Res.string.plugins_engine_d),
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.8f),
|
||||||
|
modifier = Modifier.padding(top = 4.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
Switch(
|
Switch(
|
||||||
checked = checked,
|
checked = checked,
|
||||||
onCheckedChange = onCheckedChange,
|
onCheckedChange = onCheckedChange,
|
||||||
colors = SwitchDefaults.colors(
|
|
||||||
checkedThumbColor = Color.White,
|
|
||||||
checkedTrackColor = Color.White.copy(alpha = 0.35f),
|
|
||||||
uncheckedThumbColor = Color.White,
|
|
||||||
uncheckedTrackColor = Color.Black.copy(alpha = 0.25f),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun PluginCard(
|
private fun PluginCard(
|
||||||
manifest: PluginManifest,
|
manifest: PluginManifest,
|
||||||
@@ -327,10 +342,12 @@ private fun PluginCard(
|
|||||||
onDelete: () -> Unit,
|
onDelete: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Surface(
|
Card(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = modifier.fillMaxWidth(),
|
||||||
shape = RoundedCornerShape(16.dp),
|
shape = MaterialTheme.shapes.extraLarge,
|
||||||
color = PluginCardBackground,
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
Column(Modifier.padding(16.dp)) {
|
Column(Modifier.padding(16.dp)) {
|
||||||
Row(
|
Row(
|
||||||
@@ -338,75 +355,66 @@ private fun PluginCard(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
) {
|
) {
|
||||||
Icon(
|
ExpressiveIconFrame(
|
||||||
imageVector = Icons.Default.Extension,
|
icon = Icons.Default.Extension,
|
||||||
contentDescription = null,
|
containerSize = 48.dp,
|
||||||
tint = PluginMasterBlue,
|
iconSize = 24.dp,
|
||||||
modifier = Modifier.size(40.dp),
|
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
|
||||||
|
contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
|
||||||
|
materialPolygon = MaterialShapes.SoftBurst,
|
||||||
)
|
)
|
||||||
Switch(
|
Switch(
|
||||||
checked = enabled,
|
checked = enabled,
|
||||||
onCheckedChange = onEnabledChange,
|
onCheckedChange = onEnabledChange,
|
||||||
enabled = engineEnabled,
|
enabled = engineEnabled,
|
||||||
colors = SwitchDefaults.colors(
|
|
||||||
checkedThumbColor = PluginMasterBlue,
|
|
||||||
checkedTrackColor = PluginMasterBlue.copy(alpha = 0.45f),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(Modifier.height(8.dp))
|
Spacer(Modifier.height(12.dp))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = manifest.name,
|
text = manifest.name,
|
||||||
color = Color.White,
|
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
)
|
)
|
||||||
|
|
||||||
val authorLabel = manifest.author.removePrefix("@")
|
val authorLabel = manifest.author.removePrefix("@")
|
||||||
if (authorLabel.isNotBlank()) {
|
Text(
|
||||||
Text(
|
text = if (authorLabel.isNotBlank()) {
|
||||||
text = stringResource(
|
stringResource(
|
||||||
Res.string.plugins_version_author,
|
Res.string.plugins_version_author,
|
||||||
manifest.version,
|
manifest.version,
|
||||||
authorLabel,
|
authorLabel,
|
||||||
),
|
)
|
||||||
color = Color.White.copy(alpha = 0.55f),
|
} else {
|
||||||
style = MaterialTheme.typography.bodySmall,
|
manifest.version
|
||||||
modifier = Modifier.padding(top = 4.dp),
|
},
|
||||||
)
|
style = MaterialTheme.typography.bodySmall,
|
||||||
} else {
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
Text(
|
modifier = Modifier.padding(top = 4.dp),
|
||||||
text = manifest.version,
|
)
|
||||||
color = Color.White.copy(alpha = 0.55f),
|
|
||||||
style = MaterialTheme.typography.bodySmall,
|
|
||||||
modifier = Modifier.padding(top = 4.dp),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (manifest.description.isNotBlank()) {
|
if (manifest.description.isNotBlank()) {
|
||||||
Text(
|
Text(
|
||||||
text = manifest.description,
|
text = manifest.description,
|
||||||
color = Color.White.copy(alpha = 0.85f),
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
modifier = Modifier.padding(top = 12.dp),
|
modifier = Modifier.padding(top = 12.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val usage = manifest.usage.ifBlank {
|
val usage = manifest.usage.takeIf { it.isNotBlank() }
|
||||||
manifest.description
|
if (usage != null) {
|
||||||
}
|
|
||||||
if (usage.isNotBlank()) {
|
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(Res.string.plugins_usage),
|
text = stringResource(Res.string.plugins_usage),
|
||||||
color = Color.White,
|
|
||||||
style = MaterialTheme.typography.labelLarge,
|
style = MaterialTheme.typography.labelLarge,
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
modifier = Modifier.padding(top = 12.dp),
|
modifier = Modifier.padding(top = 12.dp),
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = usage,
|
text = usage,
|
||||||
color = Color.White.copy(alpha = 0.75f),
|
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
modifier = Modifier.padding(top = 4.dp),
|
modifier = Modifier.padding(top = 4.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -418,18 +426,33 @@ private fun PluginCard(
|
|||||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
PluginCardAction(Icons.Default.Share, onShare)
|
FilledTonalIconButton(onClick = onShare) {
|
||||||
PluginCardAction(Icons.Default.OpenInNew, onOpenSettings)
|
Icon(Icons.Default.Share, contentDescription = null)
|
||||||
PluginCardAction(
|
}
|
||||||
imageVector = if (pinned) Icons.Default.PushPin else Icons.Outlined.PushPin,
|
FilledTonalIconButton(onClick = onOpenSettings) {
|
||||||
onClick = onTogglePin,
|
Icon(Icons.AutoMirrored.Filled.OpenInNew, contentDescription = null)
|
||||||
)
|
}
|
||||||
|
FilledTonalIconButton(onClick = onTogglePin) {
|
||||||
|
Icon(
|
||||||
|
imageVector = if (pinned) Icons.Default.PushPin else Icons.Outlined.PushPin,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = if (pinned) {
|
||||||
|
MaterialTheme.colorScheme.primary
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
Spacer(Modifier.weight(1f))
|
Spacer(Modifier.weight(1f))
|
||||||
IconButton(onClick = onDelete) {
|
IconButton(
|
||||||
|
onClick = onDelete,
|
||||||
|
colors = IconButtonDefaults.iconButtonColors(
|
||||||
|
contentColor = MaterialTheme.colorScheme.error,
|
||||||
|
),
|
||||||
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Default.Delete,
|
Icons.Default.Delete,
|
||||||
contentDescription = stringResource(Res.string.plugins_delete),
|
contentDescription = stringResource(Res.string.plugins_delete),
|
||||||
tint = PluginDestructiveRed,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -437,31 +460,21 @@ private fun PluginCard(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
||||||
private fun PluginCardAction(
|
|
||||||
imageVector: androidx.compose.ui.graphics.vector.ImageVector,
|
|
||||||
onClick: () -> Unit,
|
|
||||||
) {
|
|
||||||
IconButton(onClick = onClick) {
|
|
||||||
Icon(
|
|
||||||
imageVector = imageVector,
|
|
||||||
contentDescription = null,
|
|
||||||
tint = Color.White.copy(alpha = 0.85f),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PluginDetailScreen(pluginId: String) {
|
fun PluginDetailScreen(pluginId: String) {
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
|
val scrollBehavior = rememberSettingsCollapsingScrollBehavior()
|
||||||
val plugin = PluginEngine.loadedPlugin(pluginId)
|
val plugin = PluginEngine.loadedPlugin(pluginId)
|
||||||
val settings = plugin?.instance?.createSettings().orEmpty()
|
val settings = plugin?.instance?.createSettings().orEmpty()
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
|
containerColor = MaterialTheme.colorScheme.background,
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
SettingsDetailTopBar(
|
||||||
title = {
|
title = {
|
||||||
Text(
|
Text(
|
||||||
plugin?.manifest?.name ?: pluginId,
|
plugin?.manifest?.name ?: pluginId,
|
||||||
@@ -469,11 +482,8 @@ fun PluginDetailScreen(pluginId: String) {
|
|||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
navigationIcon = {
|
onBack = { navController.navigateUp() },
|
||||||
IconButton(onClick = { navController.navigateUp() }) {
|
scrollBehavior = scrollBehavior,
|
||||||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(Res.string.back))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
@@ -483,10 +493,49 @@ fun PluginDetailScreen(pluginId: String) {
|
|||||||
.verticalScroll()
|
.verticalScroll()
|
||||||
.padding(innerPadding),
|
.padding(innerPadding),
|
||||||
) {
|
) {
|
||||||
Category(Modifier.padding(top = 16.dp)) {
|
if (plugin != null) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp, vertical = 16.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
ExpressiveIconFrame(
|
||||||
|
icon = Icons.Default.Extension,
|
||||||
|
materialPolygon = MaterialShapes.Cookie6Sided,
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(8.dp))
|
||||||
|
Text(
|
||||||
|
text = plugin.manifest.name,
|
||||||
|
style = MaterialTheme.typography.headlineSmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
)
|
||||||
|
val authorLabel = plugin.manifest.author.removePrefix("@")
|
||||||
|
if (authorLabel.isNotBlank()) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(
|
||||||
|
Res.string.plugins_version_author,
|
||||||
|
plugin.manifest.version,
|
||||||
|
authorLabel,
|
||||||
|
),
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.padding(top = 4.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Category(
|
||||||
|
modifier = Modifier.padding(top = 8.dp),
|
||||||
|
margin = PaddingValues(horizontal = 16.dp, vertical = 0.dp),
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||||
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(Res.string.plugins_settings_title),
|
text = stringResource(Res.string.plugins_settings_title),
|
||||||
modifier = Modifier.padding(horizontal = SettingsStepHorizontalPadding, vertical = 8.dp),
|
modifier = Modifier.padding(horizontal = SettingsStepHorizontalPadding, vertical = 8.dp),
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
)
|
)
|
||||||
settings.forEach { setting ->
|
settings.forEach { setting ->
|
||||||
when (setting) {
|
when (setting) {
|
||||||
@@ -494,6 +543,7 @@ fun PluginDetailScreen(pluginId: String) {
|
|||||||
Text(
|
Text(
|
||||||
text = setting.text,
|
text = setting.text,
|
||||||
modifier = Modifier.padding(horizontal = SettingsStepHorizontalPadding, vertical = 8.dp),
|
modifier = Modifier.padding(horizontal = SettingsStepHorizontalPadding, vertical = 8.dp),
|
||||||
|
style = MaterialTheme.typography.titleSmall,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is PluginSetting.Text -> {
|
is PluginSetting.Text -> {
|
||||||
@@ -528,7 +578,7 @@ fun PluginDetailScreen(pluginId: String) {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
androidx.compose.material3.OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = text,
|
value = text,
|
||||||
onValueChange = { value ->
|
onValueChange = { value ->
|
||||||
text = value
|
text = value
|
||||||
|
|||||||
Reference in New Issue
Block a user