@@ -136,6 +136,58 @@ fun TopAppBar(
136136 }
137137}
138138
139+ @Composable
140+ fun SmallTopAppBar (
141+ title : String ,
142+ modifier : Modifier = Modifier ,
143+ color : Color = MiuixTheme .colorScheme.background,
144+ navigationIcon : @Composable () -> Unit = {},
145+ actions : @Composable RowScope .() -> Unit = {},
146+ scrollBehavior : ScrollBehavior ? = null,
147+ defaultWindowInsetsPadding : Boolean = true,
148+ horizontalPadding : Dp = 28.dp
149+ ) {
150+ SideEffect {
151+ // Sets the height offset limit of the SmallTopAppBar to 0f
152+ // To ensure that the content can still scroll normally even when scrollBehavior is passed.
153+ scrollBehavior?.state?.heightOffsetLimit = 0f
154+ }
155+
156+ // Wrap the given actions in a Row.
157+ val actionsRow =
158+ @Composable {
159+ Row (
160+ horizontalArrangement = Arrangement .End ,
161+ verticalAlignment = Alignment .CenterVertically ,
162+ content = actions
163+ )
164+ }
165+ // Compose a MiuixSurface with a MiuixTopAppBarLayout content.
166+ // The surface's background color is animated as specified above.
167+ // The height of the app bar is determined by subtracting the bar's height offset from the
168+ // app bar's defined constant height value (i.e. the ContainerHeight token).
169+ Surface (
170+ color = color,
171+ modifier = if (defaultWindowInsetsPadding) {
172+ modifier
173+ .windowInsetsPadding(WindowInsets .displayCutout.only(WindowInsetsSides .Horizontal ))
174+ .windowInsetsPadding(WindowInsets .navigationBars.only(WindowInsetsSides .Horizontal ))
175+ } else {
176+ modifier
177+ }
178+ .pointerInput(Unit ) {
179+ detectVerticalDragGestures { _, _ -> }
180+ }
181+ ) {
182+ SmallTopAppBarLayout (
183+ title = title,
184+ navigationIcon = navigationIcon,
185+ actions = actionsRow,
186+ horizontalPadding = horizontalPadding
187+ )
188+ }
189+ }
190+
139191/* *
140192 * Returns a [ScrollBehavior] that adjusts its properties to affect the colors and
141193 * height of the top app bar.
@@ -472,16 +524,16 @@ private fun interface ScrolledOffset {
472524}
473525
474526/* *
475- * The base [Layout] for all top app bars . This function lays out a top app bar navigation icon
527+ * The base [Layout] for [TopAppBar] . This function lays out a top app bar navigation icon
476528 * (leading icon), a title (header), and action icons (trailing icons). Note that the navigation and
477529 * the actions are optional.
478530 *
479- * @param title the top app bar title (header)
480- * @param navigationIcon a navigation icon [Composable]
481- * @param actions actions [Composable]
482- * @param scrolledOffset a function that provides the scroll offset of the top app bar
483- * @param expandedHeightPx the expanded height of the top app bar in pixels
484- * @param horizontalPadding the horizontal padding of the [TopAppBar]
531+ * @param title the top app bar title (header).
532+ * @param navigationIcon a navigation icon [Composable].
533+ * @param actions actions [Composable].
534+ * @param scrolledOffset a function that provides the scroll offset of the top app bar.
535+ * @param expandedHeightPx the expanded height of the top app bar in pixels.
536+ * @param horizontalPadding the horizontal padding of the [TopAppBar].
485537 */
486538@Composable
487539private fun TopAppBarLayout (
@@ -629,4 +681,115 @@ private fun TopAppBarLayout(
629681 )
630682 }
631683 }
684+ }
685+
686+
687+ /* *
688+ * The base [Layout] for [SmallTopAppBar]. This function lays out a top app bar navigation icon
689+ * (leading icon), a title (header), and action icons (trailing icons). Note that the navigation and
690+ * the actions are optional.
691+ *
692+ * @param title the top app bar title (header).
693+ * @param navigationIcon a navigation icon [Composable].
694+ * @param actions actions [Composable].
695+ * @param horizontalPadding the horizontal padding of the [SmallTopAppBar].
696+ */
697+ @Composable
698+ private fun SmallTopAppBarLayout (
699+ title : String ,
700+ navigationIcon : @Composable () -> Unit ,
701+ actions : @Composable () -> Unit ,
702+ horizontalPadding : Dp
703+ ) {
704+ Layout (
705+ {
706+ Box (
707+ Modifier
708+ .layoutId(" navigationIcon" )
709+ ) {
710+ navigationIcon()
711+ }
712+ Box (
713+ Modifier
714+ .layoutId(" title" )
715+ .padding(horizontal = horizontalPadding)
716+ ) {
717+ Text (
718+ text = title,
719+ maxLines = 1 ,
720+ fontSize = MiuixTheme .textStyles.title3.fontSize,
721+ fontWeight = FontWeight .Medium
722+ )
723+ }
724+ Box (
725+ Modifier
726+ .layoutId(" actionIcons" )
727+ ) {
728+ actions()
729+ }
730+ },
731+ modifier = Modifier
732+ .windowInsetsPadding(WindowInsets .statusBars)
733+ .windowInsetsPadding(WindowInsets .captionBar.only(WindowInsetsSides .Top ))
734+ .heightIn(max = 56 .dp)
735+ ) { measurables, constraints ->
736+ val navigationIconPlaceable =
737+ measurables
738+ .fastFirst { it.layoutId == " navigationIcon" }
739+ .measure(constraints.copy(minWidth = 0 ))
740+
741+ val actionIconsPlaceable =
742+ measurables
743+ .fastFirst { it.layoutId == " actionIcons" }
744+ .measure(constraints.copy(minWidth = 0 ))
745+
746+ val maxTitleWidth =
747+ if (constraints.maxWidth == Constraints .Infinity ) {
748+ constraints.maxWidth
749+ } else {
750+ (constraints.maxWidth - navigationIconPlaceable.width - actionIconsPlaceable.width)
751+ .coerceAtLeast(0 )
752+ }
753+
754+ val titlePlaceable =
755+ measurables
756+ .fastFirst { it.layoutId == " title" }
757+ .measure(constraints.copy(minWidth = 0 , maxWidth = maxTitleWidth))
758+
759+
760+ val layoutHeight =
761+ if (constraints.maxHeight == Constraints .Infinity ) {
762+ constraints.maxHeight
763+ } else {
764+ constraints.maxHeight
765+ }
766+
767+ layout(constraints.maxWidth, layoutHeight) {
768+ val verticalCenter = 60 .dp.roundToPx() / 2
769+
770+ // Navigation icon
771+ navigationIconPlaceable.placeRelative(
772+ x = 0 ,
773+ y = verticalCenter - navigationIconPlaceable.height / 2
774+ )
775+
776+ // Title
777+ var baseX = (constraints.maxWidth - titlePlaceable.width) / 2
778+ if (baseX < navigationIconPlaceable.width) {
779+ baseX + = (navigationIconPlaceable.width - baseX)
780+ } else if (baseX + titlePlaceable.width > constraints.maxWidth - actionIconsPlaceable.width) {
781+ baseX + = ((constraints.maxWidth - actionIconsPlaceable.width) - (baseX + titlePlaceable.width))
782+ }
783+ titlePlaceable.placeRelative(
784+ x = baseX,
785+ y = verticalCenter - titlePlaceable.height / 2
786+ )
787+
788+ // Action icons
789+ actionIconsPlaceable.placeRelative(
790+ x = constraints.maxWidth - actionIconsPlaceable.width,
791+ y = verticalCenter - actionIconsPlaceable.height / 2
792+ )
793+ }
794+ }
632795}
0 commit comments