Merge pull request #2541 from vitorpamplona/claude/fix-arrowback-icon-0v6aT
Optimize Material Symbols rendering with shared font and text measurer
This commit is contained in:
@@ -61,6 +61,7 @@ import com.halilibo.richtext.ui.resolveDefaults
|
||||
import com.patrykandpatrick.vico.compose.common.VicoTheme
|
||||
import com.patrykandpatrick.vico.compose.common.VicoTheme.CandlestickCartesianLayerColors
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.ProvideMaterialSymbols
|
||||
import com.vitorpamplona.amethyst.model.ThemeType
|
||||
|
||||
private val DarkColorPalette =
|
||||
@@ -591,7 +592,7 @@ fun AmethystTheme(
|
||||
colorScheme = colors,
|
||||
typography = Typography,
|
||||
shapes = Shapes,
|
||||
content = content,
|
||||
content = { ProvideMaterialSymbols(content) },
|
||||
)
|
||||
|
||||
val view = LocalView.current
|
||||
|
||||
+9
-62
@@ -20,28 +20,11 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.commons.icons.symbols
|
||||
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.drawscope.scale
|
||||
import androidx.compose.ui.graphics.drawscope.translate
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.role
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.drawText
|
||||
import androidx.compose.ui.text.rememberTextMeasurer
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
private val IconDefaultSize = 24.dp
|
||||
import androidx.compose.material3.Icon as Material3Icon
|
||||
|
||||
@Composable
|
||||
fun Icon(
|
||||
@@ -49,49 +32,13 @@ fun Icon(
|
||||
contentDescription: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
tint: Color = LocalContentColor.current,
|
||||
weight: Int = MaterialSymbolsDefaults.WEIGHT,
|
||||
) {
|
||||
val fontFamily = rememberMaterialSymbolsFontFamily(weight)
|
||||
val textMeasurer = rememberTextMeasurer()
|
||||
val density = LocalDensity.current
|
||||
val mirror = symbol.autoMirror && LocalLayoutDirection.current == LayoutDirection.Rtl
|
||||
|
||||
val semanticsModifier =
|
||||
if (contentDescription != null) {
|
||||
Modifier.semantics {
|
||||
this.contentDescription = contentDescription
|
||||
this.role = Role.Image
|
||||
}
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
|
||||
Canvas(
|
||||
modifier =
|
||||
modifier
|
||||
.defaultMinSize(IconDefaultSize, IconDefaultSize)
|
||||
.then(semanticsModifier),
|
||||
) {
|
||||
val sizePx = size.minDimension
|
||||
val fontSize = with(density) { sizePx.toSp() }
|
||||
val layout =
|
||||
textMeasurer.measure(
|
||||
text = symbol.glyph,
|
||||
style =
|
||||
TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontSize = fontSize,
|
||||
color = tint,
|
||||
),
|
||||
)
|
||||
val tx = (size.width - layout.size.width) / 2f
|
||||
val ty = (size.height - layout.size.height) / 2f
|
||||
if (mirror) {
|
||||
scale(scaleX = -1f, scaleY = 1f, pivot = Offset(size.width / 2f, size.height / 2f)) {
|
||||
translate(tx, ty) { drawText(layout) }
|
||||
}
|
||||
} else {
|
||||
translate(tx, ty) { drawText(layout) }
|
||||
}
|
||||
}
|
||||
Material3Icon(
|
||||
painter = rememberMaterialSymbolPainter(symbol, tint),
|
||||
contentDescription = contentDescription,
|
||||
modifier = modifier,
|
||||
// Tint is already baked into the painter draw; pass Unspecified so Material3 doesn't
|
||||
// double-tint via a ColorFilter.
|
||||
tint = Color.Unspecified,
|
||||
)
|
||||
}
|
||||
|
||||
+20
-14
@@ -23,57 +23,63 @@ package com.vitorpamplona.amethyst.commons.icons.symbols
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.scale
|
||||
import androidx.compose.ui.graphics.drawscope.translate
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.text.TextMeasurer
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.drawText
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.rememberTextMeasurer
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
|
||||
@Composable
|
||||
fun rememberMaterialSymbolPainter(
|
||||
symbol: MaterialSymbol,
|
||||
tint: Color = LocalContentColor.current,
|
||||
weight: Int = MaterialSymbolsDefaults.WEIGHT,
|
||||
): Painter {
|
||||
val fontFamily = rememberMaterialSymbolsFontFamily(weight)
|
||||
val textMeasurer = rememberTextMeasurer()
|
||||
val fontFamily = materialSymbolsFontFamily()
|
||||
val textMeasurer = materialSymbolsTextMeasurer()
|
||||
val density = LocalDensity.current
|
||||
return remember(symbol, fontFamily, tint, density) {
|
||||
MaterialSymbolPainter(symbol, fontFamily, tint, textMeasurer, density)
|
||||
val rtl = LocalLayoutDirection.current == LayoutDirection.Rtl
|
||||
return remember(symbol, fontFamily, textMeasurer, density, tint, rtl) {
|
||||
MaterialSymbolPainter(symbol, fontFamily, textMeasurer, density, tint, rtl)
|
||||
}
|
||||
}
|
||||
|
||||
private class MaterialSymbolPainter(
|
||||
private val symbol: MaterialSymbol,
|
||||
private val fontFamily: FontFamily,
|
||||
private val tint: Color,
|
||||
private val textMeasurer: TextMeasurer,
|
||||
private val density: Density,
|
||||
private val tint: Color,
|
||||
private val rtl: Boolean,
|
||||
) : Painter() {
|
||||
override val intrinsicSize: Size = Size.Unspecified
|
||||
|
||||
override fun DrawScope.onDraw() {
|
||||
val sizePx = size.minDimension
|
||||
val fontSize = with(density) { sizePx.toSp() }
|
||||
// TextStyle omits color; we override at draw time so TextMeasurer's cache hits across tints.
|
||||
val layout =
|
||||
textMeasurer.measure(
|
||||
text = symbol.glyph,
|
||||
style =
|
||||
TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontSize = fontSize,
|
||||
color = tint,
|
||||
),
|
||||
style = TextStyle(fontFamily = fontFamily, fontSize = fontSize),
|
||||
)
|
||||
val tx = (size.width - layout.size.width) / 2f
|
||||
val ty = (size.height - layout.size.height) / 2f
|
||||
translate(tx, ty) { drawText(layout) }
|
||||
if (symbol.autoMirror && rtl) {
|
||||
scale(scaleX = -1f, scaleY = 1f, pivot = Offset(size.width / 2f, size.height / 2f)) {
|
||||
translate(tx, ty) { drawText(layout, color = tint) }
|
||||
}
|
||||
} else {
|
||||
translate(tx, ty) { drawText(layout, color = tint) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+56
-4
@@ -21,27 +21,79 @@
|
||||
package com.vitorpamplona.amethyst.commons.icons.symbols
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.ProvidableCompositionLocal
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.text.TextMeasurer
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontVariation
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.rememberTextMeasurer
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.material_symbols_outlined
|
||||
import org.jetbrains.compose.resources.Font
|
||||
|
||||
// Held on CompositionLocal so every Material Symbol call site in the tree reuses one FontFamily
|
||||
// instance. Without this, every Icon() composition allocates a new Font wrapper, breaks its own
|
||||
// remember cache, and forces a fresh TextMeasurer per call site.
|
||||
val LocalMaterialSymbolsFontFamily: ProvidableCompositionLocal<FontFamily?> = staticCompositionLocalOf { null }
|
||||
|
||||
// Shared TextMeasurer so its internal LRU cache is hit by every icon draw in the tree.
|
||||
val LocalMaterialSymbolsTextMeasurer: ProvidableCompositionLocal<TextMeasurer?> = staticCompositionLocalOf { null }
|
||||
|
||||
private const val TEXT_MEASURER_CACHE_SIZE = 64
|
||||
|
||||
/**
|
||||
* Builds the Material Symbols FontFamily and a shared TextMeasurer once for the subtree and
|
||||
* exposes them via CompositionLocal. Wrap app roots (AmethystTheme, desktop MaterialTheme) in this.
|
||||
*/
|
||||
@Composable
|
||||
fun rememberMaterialSymbolsFontFamily(weight: Int = MaterialSymbolsDefaults.WEIGHT): FontFamily {
|
||||
fun ProvideMaterialSymbols(content: @Composable () -> Unit) {
|
||||
val font =
|
||||
Font(
|
||||
resource = Res.font.material_symbols_outlined,
|
||||
weight = FontWeight(weight),
|
||||
weight = FontWeight(MaterialSymbolsDefaults.WEIGHT),
|
||||
variationSettings =
|
||||
FontVariation.Settings(
|
||||
FontVariation.weight(weight),
|
||||
FontVariation.weight(MaterialSymbolsDefaults.WEIGHT),
|
||||
FontVariation.Setting("FILL", MaterialSymbolsDefaults.FILL),
|
||||
FontVariation.Setting("opsz", MaterialSymbolsDefaults.OPTICAL_SIZE),
|
||||
FontVariation.Setting("GRAD", MaterialSymbolsDefaults.GRADE),
|
||||
),
|
||||
)
|
||||
return remember(font) { FontFamily(font) }
|
||||
// Keyless remember: the Font wrapper identity changes every composition but the underlying
|
||||
// resource is a compile-time constant, so one FontFamily for the lifetime of the subtree.
|
||||
val fontFamily = remember { FontFamily(font) }
|
||||
val textMeasurer = rememberTextMeasurer(cacheSize = TEXT_MEASURER_CACHE_SIZE)
|
||||
CompositionLocalProvider(
|
||||
LocalMaterialSymbolsFontFamily provides fontFamily,
|
||||
LocalMaterialSymbolsTextMeasurer provides textMeasurer,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun materialSymbolsFontFamily(): FontFamily = LocalMaterialSymbolsFontFamily.current ?: localMaterialSymbolsFontFamilyFallback()
|
||||
|
||||
@Composable
|
||||
internal fun materialSymbolsTextMeasurer(): TextMeasurer =
|
||||
LocalMaterialSymbolsTextMeasurer.current
|
||||
?: rememberTextMeasurer(cacheSize = TEXT_MEASURER_CACHE_SIZE)
|
||||
|
||||
@Composable
|
||||
private fun localMaterialSymbolsFontFamilyFallback(): FontFamily {
|
||||
val font =
|
||||
Font(
|
||||
resource = Res.font.material_symbols_outlined,
|
||||
weight = FontWeight(MaterialSymbolsDefaults.WEIGHT),
|
||||
variationSettings =
|
||||
FontVariation.Settings(
|
||||
FontVariation.weight(MaterialSymbolsDefaults.WEIGHT),
|
||||
FontVariation.Setting("FILL", MaterialSymbolsDefaults.FILL),
|
||||
FontVariation.Setting("opsz", MaterialSymbolsDefaults.OPTICAL_SIZE),
|
||||
FontVariation.Setting("GRAD", MaterialSymbolsDefaults.GRADE),
|
||||
),
|
||||
)
|
||||
return remember { FontFamily(font) }
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ import androidx.compose.ui.window.application
|
||||
import androidx.compose.ui.window.rememberWindowState
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.ProvideMaterialSymbols
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.nip17Dm.unwrapAndUnsealOrNull
|
||||
import com.vitorpamplona.amethyst.desktop.account.AccountManager
|
||||
import com.vitorpamplona.amethyst.desktop.account.AccountState
|
||||
@@ -740,148 +741,150 @@ fun App(
|
||||
MaterialTheme(
|
||||
colorScheme = darkColorScheme(),
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background,
|
||||
) {
|
||||
when (accountState) {
|
||||
is AccountState.LoggedOut -> {
|
||||
LoginScreen(
|
||||
accountManager = accountManager,
|
||||
onLoginSuccess = {
|
||||
// Start heartbeat if bunker account
|
||||
val current = accountManager.currentAccount()
|
||||
if (current?.signerType is com.vitorpamplona.amethyst.desktop.account.SignerType.Remote) {
|
||||
accountManager.startHeartbeat(scope)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
is AccountState.ConnectingRelays -> {
|
||||
val relays by relayManager.relayStatuses.collectAsState()
|
||||
ConnectingRelaysScreen(
|
||||
subtitle = "Restoring remote signer session",
|
||||
relayStatuses = relays,
|
||||
)
|
||||
}
|
||||
|
||||
is AccountState.LoggedIn -> {
|
||||
val account = accountState as AccountState.LoggedIn
|
||||
val nwcConnection by accountManager.nwcConnection.collectAsState()
|
||||
|
||||
// Load NWC connection on first composition
|
||||
LaunchedEffect(Unit) {
|
||||
accountManager.loadNwcConnection()
|
||||
}
|
||||
|
||||
val currentTorStatus = torManager.status.collectAsState().value
|
||||
androidx.compose.runtime.CompositionLocalProvider(
|
||||
com.vitorpamplona.amethyst.desktop.ui.tor.LocalTorState provides
|
||||
com.vitorpamplona.amethyst.desktop.ui.tor.TorState(
|
||||
status = currentTorStatus,
|
||||
settings = torSettings,
|
||||
onSettingsChanged = { newSettings ->
|
||||
torSettings = newSettings
|
||||
com.vitorpamplona.amethyst.desktop.tor.DesktopTorPreferences
|
||||
.save(newSettings)
|
||||
torTypeFlow.value = newSettings.torType
|
||||
externalPortFlow.value = newSettings.externalSocksPort
|
||||
// Rebuild app to apply Tor changes
|
||||
onRestartApp()
|
||||
},
|
||||
),
|
||||
) {
|
||||
MainContent(
|
||||
layoutMode = layoutMode,
|
||||
deckState = deckState,
|
||||
workspaceManager = workspaceManager,
|
||||
singlePaneState = singlePaneState,
|
||||
pinnedNavBarState = pinnedNavBarState,
|
||||
relayManager = relayManager,
|
||||
localCache = localCache,
|
||||
ProvideMaterialSymbols {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background,
|
||||
) {
|
||||
when (accountState) {
|
||||
is AccountState.LoggedOut -> {
|
||||
LoginScreen(
|
||||
accountManager = accountManager,
|
||||
account = account,
|
||||
nwcConnection = nwcConnection,
|
||||
subscriptionsCoordinator = subscriptionsCoordinator,
|
||||
nip11Fetcher = nip11Fetcher,
|
||||
appScope = scope,
|
||||
torStatus = currentTorStatus,
|
||||
onShowComposeDialog = onShowComposeDialog,
|
||||
onShowReplyDialog = onShowReplyDialog,
|
||||
onShowAppDrawer = onShowAppDrawer,
|
||||
)
|
||||
}
|
||||
|
||||
// Compose dialog
|
||||
if (showComposeDialog) {
|
||||
ComposeNoteDialog(
|
||||
onDismiss = onDismissComposeDialog,
|
||||
relayManager = relayManager,
|
||||
account = account,
|
||||
replyTo = replyToNote,
|
||||
)
|
||||
}
|
||||
|
||||
// App Drawer overlay
|
||||
if (showAppDrawer) {
|
||||
val openColumns by deckState.columns.collectAsState()
|
||||
AppDrawer(
|
||||
openColumnTypes =
|
||||
if (layoutMode == LayoutMode.DECK) {
|
||||
openColumns.map { it.type.typeKey() }.toSet()
|
||||
} else {
|
||||
emptySet()
|
||||
},
|
||||
pinnedNavBarState = pinnedNavBarState,
|
||||
workspaceManager = workspaceManager,
|
||||
onSwitchWorkspace = { ws ->
|
||||
// Switch layout mode to match workspace
|
||||
onLayoutModeChange(ws.layoutMode)
|
||||
// Load columns or single pane screen
|
||||
when (ws.layoutMode) {
|
||||
LayoutMode.DECK -> {
|
||||
deckState.loadFromWorkspace(ws.columns)
|
||||
}
|
||||
|
||||
LayoutMode.SINGLE_PANE -> {
|
||||
// Load nav bar from workspace + navigate to first screen
|
||||
pinnedNavBarState.loadFromWorkspace()
|
||||
val firstKey =
|
||||
ws.singlePaneScreens.firstOrNull() ?: "home"
|
||||
val type = DeckState.parseColumnTypeFromKey(firstKey)
|
||||
if (type != null) singlePaneState.navigate(type)
|
||||
}
|
||||
onLoginSuccess = {
|
||||
// Start heartbeat if bunker account
|
||||
val current = accountManager.currentAccount()
|
||||
if (current?.signerType is com.vitorpamplona.amethyst.desktop.account.SignerType.Remote) {
|
||||
accountManager.startHeartbeat(scope)
|
||||
}
|
||||
},
|
||||
onSelectScreen = { type ->
|
||||
when (layoutMode) {
|
||||
LayoutMode.DECK -> {
|
||||
if (deckState.hasColumnOfType(type)) {
|
||||
deckState.focusExistingColumn(type)
|
||||
} else {
|
||||
deckState.addColumn(type)
|
||||
)
|
||||
}
|
||||
|
||||
is AccountState.ConnectingRelays -> {
|
||||
val relays by relayManager.relayStatuses.collectAsState()
|
||||
ConnectingRelaysScreen(
|
||||
subtitle = "Restoring remote signer session",
|
||||
relayStatuses = relays,
|
||||
)
|
||||
}
|
||||
|
||||
is AccountState.LoggedIn -> {
|
||||
val account = accountState as AccountState.LoggedIn
|
||||
val nwcConnection by accountManager.nwcConnection.collectAsState()
|
||||
|
||||
// Load NWC connection on first composition
|
||||
LaunchedEffect(Unit) {
|
||||
accountManager.loadNwcConnection()
|
||||
}
|
||||
|
||||
val currentTorStatus = torManager.status.collectAsState().value
|
||||
androidx.compose.runtime.CompositionLocalProvider(
|
||||
com.vitorpamplona.amethyst.desktop.ui.tor.LocalTorState provides
|
||||
com.vitorpamplona.amethyst.desktop.ui.tor.TorState(
|
||||
status = currentTorStatus,
|
||||
settings = torSettings,
|
||||
onSettingsChanged = { newSettings ->
|
||||
torSettings = newSettings
|
||||
com.vitorpamplona.amethyst.desktop.tor.DesktopTorPreferences
|
||||
.save(newSettings)
|
||||
torTypeFlow.value = newSettings.torType
|
||||
externalPortFlow.value = newSettings.externalSocksPort
|
||||
// Rebuild app to apply Tor changes
|
||||
onRestartApp()
|
||||
},
|
||||
),
|
||||
) {
|
||||
MainContent(
|
||||
layoutMode = layoutMode,
|
||||
deckState = deckState,
|
||||
workspaceManager = workspaceManager,
|
||||
singlePaneState = singlePaneState,
|
||||
pinnedNavBarState = pinnedNavBarState,
|
||||
relayManager = relayManager,
|
||||
localCache = localCache,
|
||||
accountManager = accountManager,
|
||||
account = account,
|
||||
nwcConnection = nwcConnection,
|
||||
subscriptionsCoordinator = subscriptionsCoordinator,
|
||||
nip11Fetcher = nip11Fetcher,
|
||||
appScope = scope,
|
||||
torStatus = currentTorStatus,
|
||||
onShowComposeDialog = onShowComposeDialog,
|
||||
onShowReplyDialog = onShowReplyDialog,
|
||||
onShowAppDrawer = onShowAppDrawer,
|
||||
)
|
||||
}
|
||||
|
||||
// Compose dialog
|
||||
if (showComposeDialog) {
|
||||
ComposeNoteDialog(
|
||||
onDismiss = onDismissComposeDialog,
|
||||
relayManager = relayManager,
|
||||
account = account,
|
||||
replyTo = replyToNote,
|
||||
)
|
||||
}
|
||||
|
||||
// App Drawer overlay
|
||||
if (showAppDrawer) {
|
||||
val openColumns by deckState.columns.collectAsState()
|
||||
AppDrawer(
|
||||
openColumnTypes =
|
||||
if (layoutMode == LayoutMode.DECK) {
|
||||
openColumns.map { it.type.typeKey() }.toSet()
|
||||
} else {
|
||||
emptySet()
|
||||
},
|
||||
pinnedNavBarState = pinnedNavBarState,
|
||||
workspaceManager = workspaceManager,
|
||||
onSwitchWorkspace = { ws ->
|
||||
// Switch layout mode to match workspace
|
||||
onLayoutModeChange(ws.layoutMode)
|
||||
// Load columns or single pane screen
|
||||
when (ws.layoutMode) {
|
||||
LayoutMode.DECK -> {
|
||||
deckState.loadFromWorkspace(ws.columns)
|
||||
}
|
||||
|
||||
LayoutMode.SINGLE_PANE -> {
|
||||
// Load nav bar from workspace + navigate to first screen
|
||||
pinnedNavBarState.loadFromWorkspace()
|
||||
val firstKey =
|
||||
ws.singlePaneScreens.firstOrNull() ?: "home"
|
||||
val type = DeckState.parseColumnTypeFromKey(firstKey)
|
||||
if (type != null) singlePaneState.navigate(type)
|
||||
}
|
||||
}
|
||||
},
|
||||
onSelectScreen = { type ->
|
||||
when (layoutMode) {
|
||||
LayoutMode.DECK -> {
|
||||
if (deckState.hasColumnOfType(type)) {
|
||||
deckState.focusExistingColumn(type)
|
||||
} else {
|
||||
deckState.addColumn(type)
|
||||
}
|
||||
}
|
||||
|
||||
LayoutMode.SINGLE_PANE -> {
|
||||
singlePaneState.navigate(type)
|
||||
LayoutMode.SINGLE_PANE -> {
|
||||
singlePaneState.navigate(type)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onDismiss = onDismissAppDrawer,
|
||||
)
|
||||
},
|
||||
onDismiss = onDismissAppDrawer,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Force logout dialog overlay
|
||||
val forceLogoutReason by accountManager.forceLogoutReason.collectAsState()
|
||||
forceLogoutReason?.let { reason ->
|
||||
ForceLogoutDialog(
|
||||
reason = reason,
|
||||
onDismiss = { accountManager.clearForceLogoutReason() },
|
||||
)
|
||||
// Force logout dialog overlay
|
||||
val forceLogoutReason by accountManager.forceLogoutReason.collectAsState()
|
||||
forceLogoutReason?.let { reason ->
|
||||
ForceLogoutDialog(
|
||||
reason = reason,
|
||||
onDismiss = { accountManager.clearForceLogoutReason() },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user