1 /*
<lambda>null2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.tv.material3.samples
18 
19 import androidx.annotation.Sampled
20 import androidx.compose.foundation.background
21 import androidx.compose.foundation.layout.Box
22 import androidx.compose.foundation.layout.fillMaxSize
23 import androidx.compose.foundation.layout.padding
24 import androidx.compose.runtime.Composable
25 import androidx.compose.runtime.LaunchedEffect
26 import androidx.compose.runtime.getValue
27 import androidx.compose.runtime.key
28 import androidx.compose.runtime.mutableStateOf
29 import androidx.compose.runtime.remember
30 import androidx.compose.runtime.setValue
31 import androidx.compose.ui.ExperimentalComposeUiApi
32 import androidx.compose.ui.Modifier
33 import androidx.compose.ui.focus.focusRestorer
34 import androidx.compose.ui.graphics.Color
35 import androidx.compose.ui.unit.dp
36 import androidx.compose.ui.unit.sp
37 import androidx.tv.material3.ExperimentalTvMaterial3Api
38 import androidx.tv.material3.Tab
39 import androidx.tv.material3.TabDefaults
40 import androidx.tv.material3.TabRow
41 import androidx.tv.material3.TabRowDefaults
42 import androidx.tv.material3.Text
43 import kotlin.time.Duration.Companion.microseconds
44 import kotlinx.coroutines.delay
45 
46 /** Tab row with a Pill indicator */
47 @OptIn(ExperimentalComposeUiApi::class)
48 @Composable
49 @Sampled
50 fun PillIndicatorTabRow() {
51     val tabs = listOf("Tab 1", "Tab 2", "Tab 3")
52     var selectedTabIndex by remember { mutableStateOf(0) }
53 
54     TabRow(selectedTabIndex = selectedTabIndex, modifier = Modifier.focusRestorer()) {
55         tabs.forEachIndexed { index, tab ->
56             key(index) {
57                 Tab(
58                     selected = index == selectedTabIndex,
59                     onFocus = { selectedTabIndex = index },
60                 ) {
61                     Text(
62                         text = tab,
63                         fontSize = 12.sp,
64                         modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp)
65                     )
66                 }
67             }
68         }
69     }
70 }
71 
72 /** Tab row with an Underlined indicator */
73 @OptIn(ExperimentalTvMaterial3Api::class, ExperimentalComposeUiApi::class)
74 @Composable
75 @Sampled
UnderlinedIndicatorTabRownull76 fun UnderlinedIndicatorTabRow() {
77     val tabs = listOf("Tab 1", "Tab 2", "Tab 3")
78     var selectedTabIndex by remember { mutableStateOf(0) }
79 
80     TabRow(
81         selectedTabIndex = selectedTabIndex,
82         indicator = { tabPositions, doesTabRowHaveFocus ->
83             TabRowDefaults.UnderlinedIndicator(
84                 currentTabPosition = tabPositions[selectedTabIndex],
85                 doesTabRowHaveFocus = doesTabRowHaveFocus,
86             )
87         },
88         modifier = Modifier.focusRestorer()
89     ) {
90         tabs.forEachIndexed { index, tab ->
91             key(index) {
92                 Tab(
93                     selected = index == selectedTabIndex,
94                     onFocus = { selectedTabIndex = index },
95                     colors = TabDefaults.underlinedIndicatorTabColors(),
96                 ) {
97                     Text(
98                         text = tab,
99                         fontSize = 12.sp,
100                         modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp)
101                     )
102                 }
103             }
104         }
105     }
106 }
107 
108 /** Tab row with delay between tab changes */
109 @OptIn(ExperimentalTvMaterial3Api::class, ExperimentalComposeUiApi::class)
110 @Composable
111 @Sampled
TabRowWithDebouncenull112 fun TabRowWithDebounce() {
113     val tabs = listOf("Tab 1", "Tab 2", "Tab 3")
114     var selectedTabIndex by remember { mutableStateOf(0) }
115 
116     // This index will be used to show a panel
117     var tabPanelIndex by remember { mutableStateOf(selectedTabIndex) }
118 
119     // Change the tab-panel only after some delay
120     LaunchedEffect(selectedTabIndex) {
121         delay(250.microseconds)
122         tabPanelIndex = selectedTabIndex
123     }
124 
125     TabRow(selectedTabIndex = selectedTabIndex, modifier = Modifier.focusRestorer()) {
126         tabs.forEachIndexed { index, tab ->
127             key(index) {
128                 Tab(
129                     selected = index == selectedTabIndex,
130                     onFocus = { selectedTabIndex = index },
131                 ) {
132                     Text(
133                         text = tab,
134                         fontSize = 12.sp,
135                         modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp)
136                     )
137                 }
138             }
139         }
140     }
141 }
142 
143 /** Tab changes onClick instead of onFocus */
144 @OptIn(ExperimentalTvMaterial3Api::class, ExperimentalComposeUiApi::class)
145 @Composable
146 @Sampled
OnClickNavigationnull147 fun OnClickNavigation() {
148     val bgColors =
149         listOf(
150             Color(0x6a, 0x16, 0x16),
151             Color(0x6a, 0x40, 0x16),
152             Color(0x6a, 0x6a, 0x16),
153             Color(0x40, 0x6a, 0x16),
154         )
155 
156     var focusedTabIndex by remember { mutableStateOf(0) }
157     var activeTabIndex by remember { mutableStateOf(focusedTabIndex) }
158 
159     Box(modifier = Modifier.fillMaxSize().background(bgColors[activeTabIndex])) {
160         TabRow(
161             selectedTabIndex = focusedTabIndex,
162             indicator = { tabPositions, doesTabRowHaveFocus ->
163                 // FocusedTab's indicator
164                 TabRowDefaults.PillIndicator(
165                     currentTabPosition = tabPositions[focusedTabIndex],
166                     activeColor = Color.Blue.copy(alpha = 0.4f),
167                     inactiveColor = Color.Transparent,
168                     doesTabRowHaveFocus = doesTabRowHaveFocus,
169                 )
170 
171                 // SelectedTab's indicator
172                 TabRowDefaults.PillIndicator(
173                     currentTabPosition = tabPositions[activeTabIndex],
174                     doesTabRowHaveFocus = doesTabRowHaveFocus,
175                 )
176             },
177             modifier = Modifier.focusRestorer()
178         ) {
179             repeat(bgColors.size) {
180                 key(it) {
181                     Tab(
182                         selected = activeTabIndex == it,
183                         onFocus = { focusedTabIndex = it },
184                         onClick = {
185                             focusedTabIndex = it
186                             activeTabIndex = it
187                         }
188                     ) {
189                         Text(
190                             text = "Tab ${it + 1}",
191                             fontSize = 12.sp,
192                             modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp)
193                         )
194                     }
195                 }
196             }
197         }
198     }
199 }
200