Merge pull request #719 from microsoft/joypal/appBarCenterTitle

Center-aligned NavigationBar title with leading left Avatar icon button
This commit is contained in:
Joyeeta Pal 2024-10-28 15:12:50 +05:30 коммит произвёл GitHub
Родитель 851a4989a4 396e565cda
Коммит 96f934c4b9
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
4 изменённых файлов: 142 добавлений и 70 удалений

Просмотреть файл

@ -16,6 +16,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
@ -56,6 +57,9 @@ const val APP_BAR_SUBTITLE_PARAM = "App Bar Subtitle Param"
const val APP_BAR_STYLE_PARAM = "App Bar AppBar Style Param"
const val APP_BAR_BUTTONBAR_PARAM = "App Bar ButtonBar Param"
const val APP_BAR_SEARCHBAR_PARAM = "App Bar SearchBar Param"
const val APP_BAR_LOGO_PARAM = "App Bar Logo Param"
const val APP_BAR_CENTER_ALIGN_PARAM = "App Bar Center Align Param"
const val APP_BAR_NAVIGATION_ICON_PARAM = "App Bar Navigation Icon Param"
class V2AppBarActivity : V2DemoActivity() {
init {
@ -79,7 +83,10 @@ class V2AppBarActivity : V2DemoActivity() {
var enableSearchBar: Boolean by rememberSaveable { mutableStateOf(false) }
var enableButtonBar: Boolean by rememberSaveable { mutableStateOf(false) }
var enableBottomBorder: Boolean by rememberSaveable { mutableStateOf(true) }
var centerAlignAppBar: Boolean by rememberSaveable { mutableStateOf(false) }
var showNavigationIcon: Boolean by rememberSaveable { mutableStateOf(true) }
var yAxisDelta: Float by rememberSaveable { mutableStateOf(1.0F) }
var enableLogo: Boolean by rememberSaveable { mutableStateOf(true) }
Column(modifier = Modifier.pointerInput(Unit) {
detectDragGestures { _, distance ->
@ -97,6 +104,7 @@ class V2AppBarActivity : V2DemoActivity() {
chevronOrientation = ChevronOrientation(90f, 0f),
) {
Column {
ListItem.Header(LocalContext.current.resources.getString(R.string.app_bar_size))
PillBar(
mutableListOf(
PillMetaData(
@ -218,6 +226,56 @@ class V2AppBarActivity : V2DemoActivity() {
)
}
)
ListItem.Item(
text = LocalContext.current.resources.getString(R.string.left_logo),
subText = if (enableLogo)
LocalContext.current.resources.getString(R.string.fluentui_enabled)
else
LocalContext.current.resources.getString(R.string.fluentui_disabled),
trailingAccessoryContent = {
ToggleSwitch(
onValueChange = {
enableLogo = !enableLogo
},
modifier = Modifier.testTag(APP_BAR_LOGO_PARAM),
checkedState = enableLogo
)
}
)
ListItem.Item(
text = LocalContext.current.resources.getString(R.string.navigation_icon),
subText = if (showNavigationIcon)
LocalContext.current.resources.getString(R.string.fluentui_enabled)
else
LocalContext.current.resources.getString(R.string.fluentui_disabled),
trailingAccessoryContent = {
ToggleSwitch(
onValueChange = {
showNavigationIcon = !showNavigationIcon
},
modifier = Modifier.testTag(APP_BAR_NAVIGATION_ICON_PARAM),
checkedState = showNavigationIcon
)
}
)
ListItem.Item(
text = LocalContext.current.resources.getString(R.string.center_title_alignment),
subText = if (centerAlignAppBar)
LocalContext.current.resources.getString(R.string.fluentui_enabled)
else
LocalContext.current.resources.getString(R.string.fluentui_disabled),
trailingAccessoryContent = {
ToggleSwitch(
onValueChange = {
centerAlignAppBar = !centerAlignAppBar
},
modifier = Modifier.testTag(APP_BAR_CENTER_ALIGN_PARAM),
checkedState = centerAlignAppBar
)
}
)
}
}
@ -261,31 +319,39 @@ class V2AppBarActivity : V2DemoActivity() {
AppBar(
title = "Fluent UI Demo",
navigationIcon = FluentIcon(
SearchBarIcons.Arrowback,
contentDescription = "Navigate Back",
onClick = {
Toast.makeText(
context,
"Navigation Icon pressed",
Toast.LENGTH_SHORT
).show()
},
flipOnRtl = true
),
subTitle = subtitle,
logo = {
Avatar(
Person(
"Allan",
"Munger",
status = AvatarStatus.DND,
isActive = true
),
enablePresence = true,
size = AvatarSize.Size32
navigationIcon = if (showNavigationIcon) {
FluentIcon(
SearchBarIcons.Arrowback,
contentDescription = "Navigate Back",
onClick = {
Toast.makeText(
context,
"Navigation Icon pressed",
Toast.LENGTH_SHORT
).show()
},
flipOnRtl = true
)
},
} else null,
subTitle = subtitle,
centerAlignAppBar = centerAlignAppBar,
logo = if (enableLogo) {
{
Avatar(
Person(
"Allan",
"Munger",
status = AvatarStatus.DND,
isActive = true
),
enablePresence = true,
size = AvatarSize.Size32,
modifier = if (!showNavigationIcon) {
Modifier.padding(start = 16.dp)
} else Modifier
)
}
} else null,
postTitleIcon = FluentIcon(
ListItemIcons.Chevron,
contentDescription = LocalContext.current.resources.getString(R.string.fluentui_chevron),

Просмотреть файл

@ -42,6 +42,18 @@
<!-- Text that shows the action performed to the user -->
<string name="app_bar_layout_navigation_icon_clicked">Navigation icon clicked.</string>
<!-- Text for title alignment -->
<string name="center_title_alignment">Center align app bar</string>
<!-- Text for app bar size -->
<string name="app_bar_size">App Bar size</string>
<!-- Text for app bar left accessories -->
<string name="left_logo">Left Logo</string>
<!-- Text for app bar navigation icon -->
<string name="navigation_icon">Navigation Icon</string>
<!-- Icon labels -->
<string name="app_bar_layout_menu_flag">Flag</string>
<string name="app_bar_layout_menu_settings">Settings</string>

Просмотреть файл

@ -169,7 +169,6 @@ open class AppBarTokens : IControlToken, Parcelable {
AppBarSize.Large -> FluentTheme.aliasTokens.typography[FluentAliasTokens.TypographyTokens.Title1]
AppBarSize.Medium -> FluentTheme.aliasTokens.typography[FluentAliasTokens.TypographyTokens.Title2]
AppBarSize.Small -> FluentTheme.aliasTokens.typography[FluentAliasTokens.TypographyTokens.Body1Strong]
else -> TextStyle(fontSize = 0.sp)
}
}
@ -203,7 +202,7 @@ open class AppBarTokens : IControlToken, Parcelable {
@Composable
open fun navigationIconPadding(info: AppBarInfo): PaddingValues {
return when (info.appBarSize) {
AppBarSize.Large -> PaddingValues()
AppBarSize.Large -> PaddingValues(16.dp)
AppBarSize.Medium -> PaddingValues(16.dp)
AppBarSize.Small -> PaddingValues(16.dp)
}
@ -213,7 +212,7 @@ open class AppBarTokens : IControlToken, Parcelable {
open fun textPadding(info: AppBarInfo): PaddingValues {
return when (info.appBarSize) {
AppBarSize.Large -> PaddingValues(start = 12.dp)
AppBarSize.Medium -> PaddingValues()
AppBarSize.Medium -> PaddingValues(start = 8.dp)
AppBarSize.Small -> PaddingValues(start = 8.dp)
}
}

Просмотреть файл

@ -20,15 +20,11 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.heading
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.microsoft.fluentui.core.R
import com.microsoft.fluentui.icons.ListItemIcons
import com.microsoft.fluentui.icons.SearchBarIcons
import com.microsoft.fluentui.icons.appbaricons.AppBarIcons
import com.microsoft.fluentui.icons.appbaricons.appbaricons.Arrowback
import com.microsoft.fluentui.icons.listitemicons.Chevron
import com.microsoft.fluentui.theme.FluentTheme
import com.microsoft.fluentui.theme.token.*
@ -54,7 +50,6 @@ import com.microsoft.fluentui.theme.token.controlTokens.AppBarTokens
* @param subTitle Subtitle to be displayed. Default: [null]
* @param logo Composable to be placed at left of Title. Guideline is to not increase a size of 32x32. Default: [null]
* @param searchMode Boolean to enable/disable searchMode. Default: [false]
* @param navigationIcon Navigate Back Icon to be placed at extreme left. Default: [SearchBarIcons.Arrowback]
* @param postTitleIcon Icon to be placed after title making the title clickable. Default: Empty [FluentIcon]
* @param preSubtitleIcon Icon to be placed before subtitle. Default: Empty [FluentIcon]
* @param postSubtitleIcon Icon to be placed after subtitle. Default: [ListItemIcons.Chevron]
@ -64,6 +59,8 @@ import com.microsoft.fluentui.theme.token.controlTokens.AppBarTokens
* @param bottomBorder Boolean to place a bottom border on AppBar. Applies only when searchBar and bottomBar are empty. Default: [true]
* @param appTitleDelta Ratio of opening of appTitle. Used for Shychrome and other animations. Default: [1.0F]
* @param accessoryDelta Ratio of opening of accessory View. Used for Shychrome and other animations. Default: [1.0F]
* @param centerAlignAppBar boolean indicating if the app bar should be center aligned. Default: [false]
* @param navigationIcon Navigate Back Icon to be placed at extreme left. Default: [null]
* @param appBarTokens Optional Tokens for App Bar to customize it. Default: [null]
*/
@ -72,7 +69,7 @@ const val APP_BAR = "Fluent App bar"
const val APP_BAR_SUBTITLE = "Fluent App bar Subtitle"
const val APP_BAR_BOTTOM_BAR = "Fluent App bar Bottom bar"
const val APP_BAR_SEARCH_BAR = "Fluent App bar Search bar"
@OptIn(ExperimentalTextApi::class)
@Composable
fun AppBar(
title: String,
@ -82,7 +79,6 @@ fun AppBar(
subTitle: String? = null,
logo: @Composable (() -> Unit)? = null,
searchMode: Boolean = false,
navigationIcon: FluentIcon = FluentIcon(AppBarIcons.Arrowback, flipOnRtl = true),
postTitleIcon: FluentIcon = FluentIcon(),
preSubtitleIcon: FluentIcon = FluentIcon(),
postSubtitleIcon: FluentIcon = FluentIcon(
@ -95,7 +91,9 @@ fun AppBar(
bottomBorder: Boolean = true,
appTitleDelta: Float = 1.0F,
accessoryDelta: Float = 1.0F,
appBarTokens: AppBarTokens? = null
centerAlignAppBar: Boolean = false,
navigationIcon: FluentIcon? = null,
appBarTokens: AppBarTokens? = null,
) {
val themeID =
FluentTheme.themeID //Adding This only for recomposition in case of Token Updates. Unused otherwise.
@ -140,52 +138,43 @@ fun AppBar(
.fillMaxWidth()
.scale(scaleX = 1.0F, scaleY = appTitleDelta)
.alpha(if (appTitleDelta != 1.0F) appTitleDelta / 3 else 1.0F),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
if (appBarSize != AppBarSize.Large && navigationIcon.isIconAvailable()) {
if (navigationIcon !== null && navigationIcon.isIconAvailable()) {
Icon(
navigationIcon,
modifier =
Modifier.then(
if(navigationIcon.onClick != null)
Modifier.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = token.navigationIconRippleColor()),
enabled = true,
onClick = navigationIcon.onClick ?: {}
Modifier
.then(
if (navigationIcon.onClick != null)
Modifier.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = token.navigationIconRippleColor()),
enabled = true,
onClick = navigationIcon.onClick ?: {}
)
else Modifier
)
else Modifier
)
.padding(token.navigationIconPadding(appBarInfo))
.size(token.leftIconSize(appBarInfo)),
tint = token.navigationIconColor(appBarInfo)
)
}
if (appBarSize != AppBarSize.Medium) {
Box(
modifier = Modifier
.then(
if (appBarSize == AppBarSize.Large)
Modifier.padding(start = 16.dp)
else
Modifier
)
) {
logo?.invoke()
}
}
logo?.invoke()
val titleTextStyle = token.titleTypography(appBarInfo)
val subtitleTextStyle = token.subtitleTypography(appBarInfo)
val titleAlignment: Alignment.Horizontal =
if (centerAlignAppBar) Alignment.CenterHorizontally else Alignment.Start
if (appBarSize != AppBarSize.Large && !subTitle.isNullOrBlank()) {
Column(
modifier = Modifier
.weight(1F)
.padding(token.textPadding(appBarInfo))
.testTag(APP_BAR_SUBTITLE)
.testTag(APP_BAR_SUBTITLE),
horizontalAlignment = titleAlignment
) {
Row(
modifier = Modifier
@ -262,20 +251,26 @@ fun AppBar(
}
}
} else {
BasicText(
text = title,
Column(
modifier = Modifier
.padding(token.textPadding(appBarInfo))
.weight(1F)
.semantics { heading() },
style = titleTextStyle.merge(
TextStyle(
color = token.titleTextColor(appBarInfo)
)
),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
horizontalAlignment = titleAlignment
) {
BasicText(
text = title,
style = titleTextStyle.merge(
TextStyle(
color = token.titleTextColor(appBarInfo)
)
),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
if (rightAccessoryView != null) {