git.iko.soy/opencode-android
on c4151ca help sign in
82 lines 3.2 kB history raw
1package soy.iko.opencode.ui.components
3import androidx.compose.foundation.layout.Box
4import androidx.compose.foundation.layout.Column
5import androidx.compose.foundation.layout.RowScope
6import androidx.compose.foundation.layout.size
7import androidx.compose.material.icons.Icons
8import androidx.compose.material.icons.automirrored.filled.ArrowBack
9import androidx.compose.material3.ExperimentalMaterial3Api
10import androidx.compose.material3.Icon
11import androidx.compose.material3.IconButton
12import androidx.compose.material3.MaterialTheme
13import androidx.compose.material3.Text
14import androidx.compose.material3.TopAppBar
15import androidx.compose.material3.TopAppBarDefaults
16import androidx.compose.material3.TopAppBarScrollBehavior
17import androidx.compose.runtime.Composable
18import androidx.compose.ui.Alignment
19import androidx.compose.ui.Modifier
20import androidx.compose.ui.res.stringResource
21import androidx.compose.ui.text.style.TextOverflow
22import androidx.compose.ui.unit.dp
23import soy.iko.opencode.R
25/**
26 * The app's standard top bar: a title (with optional subtitle), an optional up/back button, an
27 * optional inline loading indicator, and trailing actions. Extracted so the many plain
28 * back-title-actions screens share one implementation instead of hand-rolling a [TopAppBar] each.
29 * Screens with a bespoke title (dropdown server switcher, clickable model picker) keep their own bar.
30 */
31@OptIn(ExperimentalMaterial3Api::class)
32@Composable
33fun AppTopBar(
34 title: String,
35 onBack: (() -> Unit)? = null,
36 scrollBehavior: TopAppBarScrollBehavior? = null,
37 subtitle: String? = null,
38 loading: Boolean = false,
39 actions: @Composable RowScope.() -> Unit = {},
40) {
41 TopAppBar(
42 title = {
43 if (subtitle != null) {
44 Column {
45 Text(title, maxLines = 1, overflow = TextOverflow.Ellipsis)
46 Text(
47 subtitle,
48 style = MaterialTheme.typography.labelMedium,
49 color = MaterialTheme.colorScheme.onSurfaceVariant,
50 maxLines = 1,
51 overflow = TextOverflow.Ellipsis,
52 )
53 }
54 } else {
55 Text(title, maxLines = 1, overflow = TextOverflow.Ellipsis)
56 }
57 },
58 navigationIcon = {
59 if (onBack != null) {
60 IconButton(onClick = onBack) {
61 Icon(
62 Icons.AutoMirrored.Filled.ArrowBack,
63 contentDescription = stringResource(R.string.back),
64 )
65 }
66 }
67 },
68 actions = {
69 if (loading) {
70 Box(modifier = Modifier.size(40.dp), contentAlignment = Alignment.Center) {
71 LoadingSpinner(
72 size = LoadingSize.Small,
73 color = MaterialTheme.colorScheme.onSurfaceVariant,
74 )
75 }
76 }
77 actions()
78 },
79 scrollBehavior = scrollBehavior,
80 colors = TopAppBarDefaults.topAppBarColors(),
81 )