1 /*
<lambda>null2 * Copyright 2023 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.compose.material3.adaptive.navigation
18
19 import androidx.annotation.FloatRange
20 import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
21 import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
22 import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
23 import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldDefaults
24 import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
25 import androidx.compose.material3.adaptive.layout.MutableThreePaneScaffoldState
26 import androidx.compose.material3.adaptive.layout.PaneScaffoldDirective
27 import androidx.compose.material3.adaptive.layout.SupportingPaneScaffold
28 import androidx.compose.material3.adaptive.layout.SupportingPaneScaffoldDefaults
29 import androidx.compose.material3.adaptive.layout.SupportingPaneScaffoldRole
30 import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies
31 import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem
32 import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole
33 import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState
34 import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue
35 import androidx.compose.material3.adaptive.layout.calculatePaneScaffoldDirective
36 import androidx.compose.material3.adaptive.layout.calculateThreePaneScaffoldValue
37 import androidx.compose.runtime.Composable
38 import androidx.compose.runtime.Stable
39 import androidx.compose.runtime.derivedStateOf
40 import androidx.compose.runtime.getValue
41 import androidx.compose.runtime.mutableStateListOf
42 import androidx.compose.runtime.mutableStateOf
43 import androidx.compose.runtime.saveable.Saver
44 import androidx.compose.runtime.saveable.listSaver
45 import androidx.compose.runtime.saveable.rememberSaveable
46 import androidx.compose.runtime.setValue
47 import androidx.compose.ui.util.fastMap
48 import kotlin.collections.removeLast as removeLastKt
49
50 /**
51 * The common interface of the default navigation implementations for different three-pane
52 * scaffolds.
53 *
54 * In general, we suggest you to use [rememberListDetailPaneScaffoldNavigator] or
55 * [rememberSupportingPaneScaffoldNavigator] to get remembered default instances of this interface
56 * for [ListDetailPaneScaffold] and [SupportingPaneScaffold], respectively. Those default
57 * implementations work independently from any navigation frameworks.
58 *
59 * If you need to integrate with existing navigation frameworks or implement your own custom
60 * navigation logic, usually creating whole new APIs that's tailored for your own solution will be
61 * recommended, instead of implementing this interface. But we recommend you refer to the API design
62 * and the default implementation to get better understanding and address the intricacies of
63 * navigation in an adaptive scenario.
64 *
65 * @param T the type representing the content key/id for a navigation destination. This type must be
66 * storable in a Bundle. Used to customize navigation behavior (for example,
67 * [BackNavigationBehavior]). If this customization is unneeded, you can pass [Any].
68 */
69 @ExperimentalMaterial3AdaptiveApi
70 @Stable
71 interface ThreePaneScaffoldNavigator<T> {
72 /**
73 * The current layout directives that the associated three pane scaffold needs to follow. It's
74 * supposed to be automatically updated when the window configuration changes.
75 */
76 val scaffoldDirective: PaneScaffoldDirective
77
78 /**
79 * The current state of the associated three pane scaffold, used to query the transition between
80 * layout states.
81 */
82 val scaffoldState: ThreePaneScaffoldState
83
84 /**
85 * The current layout value of the associated three pane scaffold, which represents unique
86 * layout states of the scaffold.
87 */
88 val scaffoldValue: ThreePaneScaffoldValue
89
90 /**
91 * Returns the scaffold value associated with the previous destination, assuming there is a
92 * previous destination to navigate back to. If not, this is the same as [scaffoldValue].
93 *
94 * @param backNavigationBehavior the behavior describing which backstack entries may be skipped
95 * during the back navigation. See [BackNavigationBehavior].
96 */
97 fun peekPreviousScaffoldValue(
98 backNavigationBehavior: BackNavigationBehavior =
99 BackNavigationBehavior.PopUntilScaffoldValueChange
100 ): ThreePaneScaffoldValue
101
102 /**
103 * The current destination as tracked by the navigator.
104 *
105 * Implementors of this interface should ensure this value is updated whenever a navigation
106 * operation is performed.
107 */
108 val currentDestination: ThreePaneScaffoldDestinationItem<T>?
109
110 /**
111 * Indicates if the navigator should be aware of pane destination history when deciding the
112 * result [ThreePaneScaffoldValue] by a navigation operation. If the value is `false`, only the
113 * current destination will be considered in the scaffold value calculation.
114 *
115 * @see calculateThreePaneScaffoldValue for more detailed explanation about history awareness.
116 */
117 var isDestinationHistoryAware: Boolean
118
119 /**
120 * Navigates to a new destination, possibly with an animation, and suspends until the animation
121 * is complete. The new destination is supposed to have the highest priority when calculating
122 * the new [scaffoldValue].
123 *
124 * Implementors of this interface should ensure the new destination pane will be expanded or
125 * adapted in a reasonable way so it provides users the sense that the new destination is the
126 * pane currently being used.
127 *
128 * @param pane the new destination pane.
129 * @param contentKey the optional key or id representing the content of the new destination.
130 */
131 suspend fun navigateTo(pane: ThreePaneScaffoldRole, contentKey: T? = null)
132
133 /**
134 * Returns `true` if there is a previous destination to navigate back to.
135 *
136 * Implementors of this interface should ensure the logic of this function is consistent with
137 * [navigateBack].
138 *
139 * @param backNavigationBehavior the behavior describing which backstack entries may be skipped
140 * during the back navigation. See [BackNavigationBehavior].
141 */
142 fun canNavigateBack(
143 backNavigationBehavior: BackNavigationBehavior =
144 BackNavigationBehavior.PopUntilScaffoldValueChange
145 ): Boolean
146
147 /**
148 * Navigates to the previous destination, possibly with an animation, and suspends until the
149 * animation is complete. Returns `true` if there is a previous destination to navigate back to.
150 *
151 * Implementors of this interface should ensure the logic of this function is consistent with
152 * [canNavigateBack].
153 *
154 * @param backNavigationBehavior the behavior describing which backstack entries may be skipped
155 * during the back navigation. See [BackNavigationBehavior].
156 */
157 suspend fun navigateBack(
158 backNavigationBehavior: BackNavigationBehavior =
159 BackNavigationBehavior.PopUntilScaffoldValueChange
160 ): Boolean
161
162 /**
163 * Seeks the [scaffoldState] transition to the previous destination, as in a predictive back
164 * animation.
165 *
166 * This does not affect the current [scaffoldValue] or backstack. To do so, call [navigateBack]
167 * when the back navigation action is finalized.
168 *
169 * @param backNavigationBehavior the behavior describing which backstack entries may be skipped
170 * during the back navigation. See [BackNavigationBehavior].
171 * @param fraction the progress fraction of the transition of backwards navigation.
172 */
173 suspend fun seekBack(
174 backNavigationBehavior: BackNavigationBehavior =
175 BackNavigationBehavior.PopUntilScaffoldValueChange,
176 @FloatRange(from = 0.0, to = 1.0) fraction: Float = 1.0f,
177 )
178 }
179
180 /**
181 * Returns a remembered default implementation of [ThreePaneScaffoldNavigator] for
182 * [ListDetailPaneScaffold], which will be updated automatically when the input values change. The
183 * default navigator is supposed to be used independently from any navigation frameworks and handles
184 * the navigation purely inside the [ListDetailPaneScaffold].
185 *
186 * @param T the type representing the content key/id for a navigation destination. This type must be
187 * storable in a Bundle. Used to customize navigation behavior (for example,
188 * [BackNavigationBehavior]). If this customization is unneeded, you can pass [Any].
189 * @param scaffoldDirective the current layout directives to follow. The default value will be
190 * calculated with [calculatePaneScaffoldDirective] using
191 * [WindowAdaptiveInfo][androidx.compose.material3.adaptive.WindowAdaptiveInfo] retrieved from the
192 * current context.
193 * @param adaptStrategies adaptation strategies of each pane.
194 * @param isDestinationHistoryAware `true` if the scaffold value calculation should be aware of the
195 * full destination history, instead of just the current destination. See
196 * [calculateThreePaneScaffoldValue] for more relevant details.
197 * @param initialDestinationHistory the initial pane destination history of the scaffold, by default
198 * it will be just the list pane.
199 */
200 @ExperimentalMaterial3AdaptiveApi
201 @Composable
rememberListDetailPaneScaffoldNavigatornull202 fun <T> rememberListDetailPaneScaffoldNavigator(
203 scaffoldDirective: PaneScaffoldDirective =
204 calculatePaneScaffoldDirective(currentWindowAdaptiveInfo()),
205 adaptStrategies: ThreePaneScaffoldAdaptStrategies =
206 ListDetailPaneScaffoldDefaults.adaptStrategies(),
207 isDestinationHistoryAware: Boolean = true,
208 initialDestinationHistory: List<ThreePaneScaffoldDestinationItem<T>> =
209 DefaultListDetailPaneHistory,
210 ): ThreePaneScaffoldNavigator<T> =
211 rememberThreePaneScaffoldNavigator(
212 scaffoldDirective,
213 adaptStrategies,
214 isDestinationHistoryAware,
215 initialDestinationHistory
216 )
217
218 /**
219 * Returns a remembered default implementation of [ThreePaneScaffoldNavigator] for
220 * [ListDetailPaneScaffold], which will be updated automatically when the input values change. The
221 * default navigator is supposed to be used independently from any navigation frameworks and handles
222 * the navigation purely inside the [ListDetailPaneScaffold].
223 *
224 * @param scaffoldDirective the current layout directives to follow. The default value will be
225 * calculated with [calculatePaneScaffoldDirective] using
226 * [WindowAdaptiveInfo][androidx.compose.material3.adaptive.WindowAdaptiveInfo] retrieved from the
227 * current context.
228 * @param adaptStrategies adaptation strategies of each pane.
229 * @param isDestinationHistoryAware `true` if the scaffold value calculation should be aware of the
230 * full destination history, instead of just the current destination. See
231 * [calculateThreePaneScaffoldValue] for more relevant details.
232 */
233 @ExperimentalMaterial3AdaptiveApi
234 @Composable
235 fun rememberListDetailPaneScaffoldNavigator(
236 scaffoldDirective: PaneScaffoldDirective =
237 calculatePaneScaffoldDirective(currentWindowAdaptiveInfo()),
238 adaptStrategies: ThreePaneScaffoldAdaptStrategies =
239 ListDetailPaneScaffoldDefaults.adaptStrategies(),
240 isDestinationHistoryAware: Boolean = true,
241 ): ThreePaneScaffoldNavigator<Any> =
242 rememberListDetailPaneScaffoldNavigator<Any>(
243 scaffoldDirective,
244 adaptStrategies,
245 isDestinationHistoryAware,
246 )
247
248 /**
249 * Returns a remembered default implementation of [ThreePaneScaffoldNavigator] for
250 * [SupportingPaneScaffold], which will be updated automatically when the input values change. The
251 * default navigator is supposed to be used independently from any navigation frameworks and handles
252 * the navigation purely inside the [SupportingPaneScaffold].
253 *
254 * @param T the type representing the content key/id for a navigation destination. This type must be
255 * storable in a Bundle. Used to customize navigation behavior (for example,
256 * [BackNavigationBehavior]). If this customization is unneeded, you can pass [Any].
257 * @param scaffoldDirective the current layout directives to follow. The default value will be
258 * calculated with [calculatePaneScaffoldDirective] using
259 * [WindowAdaptiveInfo][androidx.compose.material3.adaptive.WindowAdaptiveInfo] retrieved from the
260 * current context.
261 * @param adaptStrategies adaptation strategies of each pane.
262 * @param isDestinationHistoryAware `true` if the scaffold value calculation should be aware of the
263 * full destination history, instead of just the current destination. See
264 * [calculateThreePaneScaffoldValue] for more relevant details.
265 * @param initialDestinationHistory the initial destination history of the scaffold, by default it
266 * will be just the main pane.
267 */
268 @ExperimentalMaterial3AdaptiveApi
269 @Composable
270 fun <T> rememberSupportingPaneScaffoldNavigator(
271 scaffoldDirective: PaneScaffoldDirective =
272 calculatePaneScaffoldDirective(currentWindowAdaptiveInfo()),
273 adaptStrategies: ThreePaneScaffoldAdaptStrategies =
274 SupportingPaneScaffoldDefaults.adaptStrategies(),
275 isDestinationHistoryAware: Boolean = true,
276 initialDestinationHistory: List<ThreePaneScaffoldDestinationItem<T>> =
277 DefaultSupportingPaneHistory,
278 ): ThreePaneScaffoldNavigator<T> =
279 rememberThreePaneScaffoldNavigator(
280 scaffoldDirective,
281 adaptStrategies,
282 isDestinationHistoryAware,
283 initialDestinationHistory
284 )
285
286 /**
287 * Returns a remembered default implementation of [ThreePaneScaffoldNavigator] for
288 * [SupportingPaneScaffold], which will be updated automatically when the input values change. The
289 * default navigator is supposed to be used independently from any navigation frameworks and handles
290 * the navigation purely inside the [SupportingPaneScaffold].
291 *
292 * @param scaffoldDirective the current layout directives to follow. The default value will be
293 * calculated with [calculatePaneScaffoldDirective] using
294 * [WindowAdaptiveInfo][androidx.compose.material3.adaptive.WindowAdaptiveInfo] retrieved from the
295 * current context.
296 * @param adaptStrategies adaptation strategies of each pane.
297 * @param isDestinationHistoryAware `true` if the scaffold value calculation should be aware of the
298 * full destination history, instead of just the current destination. See
299 * [calculateThreePaneScaffoldValue] for more relevant details.
300 */
301 @ExperimentalMaterial3AdaptiveApi
302 @Composable
303 fun rememberSupportingPaneScaffoldNavigator(
304 scaffoldDirective: PaneScaffoldDirective =
305 calculatePaneScaffoldDirective(currentWindowAdaptiveInfo()),
306 adaptStrategies: ThreePaneScaffoldAdaptStrategies =
307 SupportingPaneScaffoldDefaults.adaptStrategies(),
308 isDestinationHistoryAware: Boolean = true,
309 ): ThreePaneScaffoldNavigator<Any> =
310 rememberSupportingPaneScaffoldNavigator<Any>(
311 scaffoldDirective,
312 adaptStrategies,
313 isDestinationHistoryAware,
314 )
315
316 @ExperimentalMaterial3AdaptiveApi
317 @Composable
318 internal fun <T> rememberThreePaneScaffoldNavigator(
319 scaffoldDirective: PaneScaffoldDirective,
320 adaptStrategies: ThreePaneScaffoldAdaptStrategies,
321 isDestinationHistoryAware: Boolean,
322 initialDestinationHistory: List<ThreePaneScaffoldDestinationItem<T>>
323 ): ThreePaneScaffoldNavigator<T> =
324 rememberSaveable(
325 saver =
326 DefaultThreePaneScaffoldNavigator.saver(
327 scaffoldDirective,
328 adaptStrategies,
329 isDestinationHistoryAware
330 )
331 ) {
332 DefaultThreePaneScaffoldNavigator(
333 initialDestinationHistory = initialDestinationHistory,
334 initialScaffoldDirective = scaffoldDirective,
335 initialAdaptStrategies = adaptStrategies,
336 initialIsDestinationHistoryAware = isDestinationHistoryAware
337 )
338 }
<lambda>null339 .apply {
340 this.scaffoldDirective = scaffoldDirective
341 this.adaptStrategies = adaptStrategies
342 this.isDestinationHistoryAware = isDestinationHistoryAware
343 }
344
345 @OptIn(ExperimentalMaterial3AdaptiveApi::class)
346 internal class DefaultThreePaneScaffoldNavigator<T>(
347 initialDestinationHistory: List<ThreePaneScaffoldDestinationItem<T>>,
348 initialScaffoldDirective: PaneScaffoldDirective,
349 initialAdaptStrategies: ThreePaneScaffoldAdaptStrategies,
350 initialIsDestinationHistoryAware: Boolean
351 ) : ThreePaneScaffoldNavigator<T> {
352
353 private val destinationHistory =
<lambda>null354 mutableStateListOf<ThreePaneScaffoldDestinationItem<T>>().apply {
355 addAll(initialDestinationHistory)
356 }
357
358 override var scaffoldDirective by mutableStateOf(initialScaffoldDirective)
359
360 override var isDestinationHistoryAware by mutableStateOf(initialIsDestinationHistoryAware)
361
362 var adaptStrategies by mutableStateOf(initialAdaptStrategies)
363
364 override val currentDestination
365 get() = destinationHistory.lastOrNull()
366
<lambda>null367 override val scaffoldValue by derivedStateOf {
368 calculateScaffoldValue(destinationHistory.lastIndex)
369 }
370
371 // Must be updated whenever `destinationHistory` changes to keep in sync.
372 override val scaffoldState = MutableThreePaneScaffoldState(scaffoldValue)
373
peekPreviousScaffoldValuenull374 override fun peekPreviousScaffoldValue(
375 backNavigationBehavior: BackNavigationBehavior
376 ): ThreePaneScaffoldValue {
377 val index = getPreviousDestinationIndex(backNavigationBehavior)
378 return if (index == -1) scaffoldValue else calculateScaffoldValue(index)
379 }
380
navigateTonull381 override suspend fun navigateTo(pane: ThreePaneScaffoldRole, contentKey: T?) {
382 destinationHistory.add(ThreePaneScaffoldDestinationItem(pane, contentKey))
383 animateStateToCurrentScaffoldValue()
384 }
385
canNavigateBacknull386 override fun canNavigateBack(backNavigationBehavior: BackNavigationBehavior): Boolean =
387 getPreviousDestinationIndex(backNavigationBehavior) >= 0
388
389 override suspend fun navigateBack(
390 backNavigationBehavior: BackNavigationBehavior,
391 ): Boolean {
392 val previousDestinationIndex = getPreviousDestinationIndex(backNavigationBehavior)
393 if (previousDestinationIndex < 0) {
394 destinationHistory.clear()
395 animateStateToCurrentScaffoldValue()
396 return false
397 }
398 val targetSize = previousDestinationIndex + 1
399 while (destinationHistory.size > targetSize) {
400 destinationHistory.removeLastKt()
401 }
402 animateStateToCurrentScaffoldValue()
403 return true
404 }
405
seekBacknull406 override suspend fun seekBack(backNavigationBehavior: BackNavigationBehavior, fraction: Float) {
407 if (fraction == 0f) {
408 animateStateToCurrentScaffoldValue()
409 } else {
410 val previousScaffoldValue = peekPreviousScaffoldValue(backNavigationBehavior)
411 scaffoldState.seekTo(fraction, previousScaffoldValue, isPredictiveBackInProgress = true)
412 }
413 }
414
animateStateToCurrentScaffoldValuenull415 private suspend fun animateStateToCurrentScaffoldValue() {
416 scaffoldState.animateTo(scaffoldValue)
417 }
418
getPreviousDestinationIndexnull419 private fun getPreviousDestinationIndex(backNavBehavior: BackNavigationBehavior): Int {
420 if (destinationHistory.size <= 1) {
421 // No previous destination
422 return -1
423 }
424 when (backNavBehavior) {
425 BackNavigationBehavior.PopLatest -> return destinationHistory.lastIndex - 1
426 BackNavigationBehavior.PopUntilScaffoldValueChange ->
427 for (previousDestinationIndex in destinationHistory.lastIndex - 1 downTo 0) {
428 val previousValue = calculateScaffoldValue(previousDestinationIndex)
429 if (previousValue != scaffoldValue) {
430 return previousDestinationIndex
431 }
432 }
433 BackNavigationBehavior.PopUntilCurrentDestinationChange ->
434 for (previousDestinationIndex in destinationHistory.lastIndex - 1 downTo 0) {
435 val destination = destinationHistory[previousDestinationIndex].pane
436 if (destination != currentDestination?.pane) {
437 return previousDestinationIndex
438 }
439 }
440 BackNavigationBehavior.PopUntilContentChange ->
441 for (previousDestinationIndex in destinationHistory.lastIndex - 1 downTo 0) {
442 val contentKey = destinationHistory[previousDestinationIndex].contentKey
443 if (contentKey != currentDestination?.contentKey) {
444 return previousDestinationIndex
445 }
446 // A scaffold value change also counts as a content change.
447 val previousValue = calculateScaffoldValue(previousDestinationIndex)
448 if (previousValue != scaffoldValue) {
449 return previousDestinationIndex
450 }
451 }
452 }
453
454 return -1
455 }
456
calculateScaffoldValuenull457 private fun calculateScaffoldValue(destinationIndex: Int) =
458 if (destinationIndex == -1) {
459 calculateThreePaneScaffoldValue(
460 maxHorizontalPartitions = scaffoldDirective.maxHorizontalPartitions,
461 maxVerticalPartitions = scaffoldDirective.maxVerticalPartitions,
462 adaptStrategies = adaptStrategies,
463 currentDestination = null
464 )
465 } else if (isDestinationHistoryAware) {
466 calculateThreePaneScaffoldValue(
467 maxHorizontalPartitions = scaffoldDirective.maxHorizontalPartitions,
468 maxVerticalPartitions = scaffoldDirective.maxVerticalPartitions,
469 adaptStrategies = adaptStrategies,
470 destinationHistory = destinationHistory.subList(0, destinationIndex + 1)
471 )
472 } else {
473 calculateThreePaneScaffoldValue(
474 maxHorizontalPartitions = scaffoldDirective.maxHorizontalPartitions,
475 maxVerticalPartitions = scaffoldDirective.maxVerticalPartitions,
476 adaptStrategies = adaptStrategies,
477 currentDestination = destinationHistory[destinationIndex]
478 )
479 }
480
481 companion object {
482 /** To keep destination history saved */
savernull483 fun <T> saver(
484 initialScaffoldDirective: PaneScaffoldDirective,
485 initialAdaptStrategies: ThreePaneScaffoldAdaptStrategies,
486 initialDestinationHistoryAware: Boolean
487 ): Saver<DefaultThreePaneScaffoldNavigator<T>, *> {
488 val destinationItemSaver = destinationItemSaver<T>()
489 return listSaver(
490 save = {
491 it.destinationHistory.fastMap { destination ->
492 with(destinationItemSaver) { save(destination) }
493 }
494 },
495 restore = {
496 DefaultThreePaneScaffoldNavigator(
497 initialDestinationHistory =
498 it.fastMap { savedDestination ->
499 destinationItemSaver.restore(savedDestination!!)!!
500 },
501 initialScaffoldDirective = initialScaffoldDirective,
502 initialAdaptStrategies = initialAdaptStrategies,
503 initialIsDestinationHistoryAware = initialDestinationHistoryAware
504 )
505 }
506 )
507 }
508 }
509 }
510
511 @OptIn(ExperimentalMaterial3AdaptiveApi::class)
destinationItemSavernull512 internal fun <T> destinationItemSaver(): Saver<ThreePaneScaffoldDestinationItem<T>, Any> =
513 listSaver(
514 save = { listOf(it.pane, it.contentKey) },
<lambda>null515 restore = {
516 @Suppress("UNCHECKED_CAST")
517 (ThreePaneScaffoldDestinationItem(
518 pane = it[0] as ThreePaneScaffoldRole,
519 contentKey = it[1] as T?
520 ))
521 }
522 )
523
524 @OptIn(ExperimentalMaterial3AdaptiveApi::class)
525 private val DefaultListDetailPaneHistory: List<ThreePaneScaffoldDestinationItem<Nothing>> =
526 listOf(ThreePaneScaffoldDestinationItem(ListDetailPaneScaffoldRole.List))
527
528 @OptIn(ExperimentalMaterial3AdaptiveApi::class)
529 private val DefaultSupportingPaneHistory: List<ThreePaneScaffoldDestinationItem<Nothing>> =
530 listOf(ThreePaneScaffoldDestinationItem(SupportingPaneScaffoldRole.Main))
531