For #25891 - Move GridViewHolder to compose
parent
263e4d819b
commit
335159cef5
@ -0,0 +1,125 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.compose
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.mozilla.fenix.theme.FirefoxTheme
|
||||
|
||||
/**
|
||||
* Displays the [content] with the right edge fading.
|
||||
*
|
||||
* @param modifier [Modifier] for the container.
|
||||
* @param fadeWidth Length of the fading edge.
|
||||
* @param backgroundColor [Color] of the background shown under the content.
|
||||
* @param isContentRtl Whether or not the content should be displayed Right to Left
|
||||
* @param content The content whose right edge must be faded.
|
||||
*/
|
||||
@Composable
|
||||
fun HorizontalFadingEdgeBox(
|
||||
modifier: Modifier = Modifier,
|
||||
fadeWidth: Dp = 25.dp,
|
||||
backgroundColor: Color = Color.Transparent,
|
||||
isContentRtl: Boolean = false,
|
||||
content: @Composable BoxScope.() -> Unit
|
||||
) {
|
||||
// List of colors defining the direction of the fade effect
|
||||
val colorList = listOf(Color.Transparent, backgroundColor)
|
||||
|
||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
|
||||
Box(modifier) {
|
||||
content()
|
||||
Spacer(
|
||||
Modifier
|
||||
.width(fadeWidth)
|
||||
.fillMaxHeight()
|
||||
.align(
|
||||
if (isContentRtl) {
|
||||
Alignment.CenterStart
|
||||
} else {
|
||||
Alignment.CenterEnd
|
||||
}
|
||||
)
|
||||
.background(
|
||||
Brush.horizontalGradient(
|
||||
colors = if (isContentRtl) {
|
||||
colorList.reversed()
|
||||
} else {
|
||||
colorList
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun FadingRightTextPreview() {
|
||||
FirefoxTheme {
|
||||
Surface(modifier = Modifier.background(FirefoxTheme.colors.layer1)) {
|
||||
HorizontalFadingEdgeBox(
|
||||
modifier = Modifier
|
||||
.width(250.dp)
|
||||
.height(20.dp)
|
||||
.clipToBounds(),
|
||||
backgroundColor = FirefoxTheme.colors.layer1
|
||||
) {
|
||||
Text(
|
||||
"Example text set to fade on the right",
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
softWrap = false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun FadingLeftTextPreview() {
|
||||
FirefoxTheme {
|
||||
Surface(modifier = Modifier.background(FirefoxTheme.colors.layer1)) {
|
||||
HorizontalFadingEdgeBox(
|
||||
modifier = Modifier
|
||||
.width(250.dp)
|
||||
.height(20.dp)
|
||||
.clipToBounds(),
|
||||
isContentRtl = true,
|
||||
fadeWidth = 50.dp,
|
||||
backgroundColor = FirefoxTheme.colors.layer1
|
||||
) {
|
||||
Text(
|
||||
"Example text set to fade on the left",
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
softWrap = false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.compose.tabstray
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import mozilla.components.concept.base.images.ImageLoadRequest
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.compose.inComposePreview
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.theme.FirefoxTheme
|
||||
import org.mozilla.fenix.theme.Theme
|
||||
|
||||
/**
|
||||
* Card which will display the thumbnail for a tab. If a thumbnail is not available for the [tabId],
|
||||
* the favicon [R.drawable.mozac_ic_globe] icon will be displayed.
|
||||
*
|
||||
* @param tabId Key used to remember the thumbnail for future compositions.
|
||||
* @param modifier [Modifier] used to draw the image content.
|
||||
* @param contentDescription Text used by accessibility services
|
||||
* to describe what this image represents.
|
||||
* @param contentScale [ContentScale] used to draw image content.
|
||||
* @param alignment [Alignment] used to draw the image content.
|
||||
*/
|
||||
@Composable
|
||||
@Suppress("LongParameterList")
|
||||
fun GridTabThumbnail(
|
||||
tabId: String,
|
||||
size: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
contentDescription: String? = null,
|
||||
contentScale: ContentScale = ContentScale.FillWidth,
|
||||
alignment: Alignment = Alignment.TopCenter
|
||||
) {
|
||||
Card(
|
||||
modifier = modifier,
|
||||
backgroundColor = FirefoxTheme.colors.layer2
|
||||
) {
|
||||
if (inComposePreview) {
|
||||
GlobeIcon()
|
||||
} else {
|
||||
val rememberBitmap = remember(tabId) { mutableStateOf<ImageBitmap?>(null) }
|
||||
val imageSize = LocalDensity.current.run { size.toPx().toInt() }
|
||||
val request = ImageLoadRequest(tabId, imageSize)
|
||||
val storage = LocalContext.current.components.core.thumbnailStorage
|
||||
val bitmap = rememberBitmap.value
|
||||
|
||||
LaunchedEffect(tabId) {
|
||||
rememberBitmap.value = storage.loadThumbnail(request).await()?.asImageBitmap()
|
||||
}
|
||||
|
||||
if (bitmap != null) {
|
||||
val painter = BitmapPainter(bitmap)
|
||||
Image(
|
||||
painter = painter,
|
||||
contentDescription = contentDescription,
|
||||
modifier = modifier,
|
||||
contentScale = contentScale,
|
||||
alignment = alignment
|
||||
)
|
||||
} else {
|
||||
GlobeIcon()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Globe icon to be displayed when no thumbnail is available.
|
||||
*/
|
||||
@Composable
|
||||
private fun GlobeIcon() {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.mozac_ic_globe),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(22.dp)
|
||||
.fillMaxSize(),
|
||||
tint = FirefoxTheme.colors.iconSecondary
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun ThumbnailCardPreview() {
|
||||
FirefoxTheme(theme = Theme.getTheme()) {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(300.dp)
|
||||
) {
|
||||
GridTabThumbnail(
|
||||
tabId = "1",
|
||||
size = LocalConfiguration.current.screenWidthDp.dp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,281 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.compose.tabstray
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredHeight
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextDirection
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.text.BidiFormatter
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.browser.state.state.createTab
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.compose.Favicon
|
||||
import org.mozilla.fenix.compose.HorizontalFadingEdgeBox
|
||||
import org.mozilla.fenix.theme.FirefoxTheme
|
||||
|
||||
/**
|
||||
* Tab grid item used to display a tab that supports clicks,
|
||||
* long clicks, multiple selection, and media controls.
|
||||
*
|
||||
* @param tab The given tab to be render as view a grid item.
|
||||
* @param isSelected Indicates if the item should be render as selected.
|
||||
* @param multiSelectionEnabled Indicates if the item should be render with multi selection options,
|
||||
* enabled.
|
||||
* @param multiSelectionSelected Indicates if the item should be render as multi selection selected
|
||||
* option.
|
||||
* @param onCloseClick Callback to handle the click event of the close button.
|
||||
* @param onMediaClick Callback to handle when the media item is clicked.
|
||||
* @param onClick Callback to handle when item is clicked.
|
||||
* @param onLongClick Callback to handle when item is long clicked.
|
||||
*/
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
@Suppress("MagicNumber", "LongParameterList", "LongMethod")
|
||||
fun TabGridItem(
|
||||
tab: TabSessionState,
|
||||
isSelected: Boolean = false,
|
||||
multiSelectionEnabled: Boolean = false,
|
||||
multiSelectionSelected: Boolean = false,
|
||||
onCloseClick: (tab: TabSessionState) -> Unit,
|
||||
onMediaClick: (tab: TabSessionState) -> Unit,
|
||||
onClick: (tab: TabSessionState) -> Unit,
|
||||
onLongClick: (tab: TabSessionState) -> Unit,
|
||||
) {
|
||||
val tabBorderModifier = if (isSelected && !multiSelectionEnabled) {
|
||||
Modifier.border(
|
||||
4.dp,
|
||||
FirefoxTheme.colors.borderAccent,
|
||||
RoundedCornerShape(12.dp)
|
||||
)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.wrapContentHeight()
|
||||
.wrapContentWidth()
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(202.dp)
|
||||
.padding(4.dp)
|
||||
.then(tabBorderModifier)
|
||||
.padding(4.dp)
|
||||
.combinedClickable(
|
||||
onLongClick = { onLongClick(tab) },
|
||||
onClick = { onClick(tab) }
|
||||
),
|
||||
elevation = 0.dp,
|
||||
shape = RoundedCornerShape(dimensionResource(id = R.dimen.tab_tray_grid_item_border_radius)),
|
||||
border = BorderStroke(1.dp, FirefoxTheme.colors.borderPrimary)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.background(FirefoxTheme.colors.layer2)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
) {
|
||||
Favicon(
|
||||
url = tab.content.url,
|
||||
size = 16.dp,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
.padding(start = 8.dp)
|
||||
)
|
||||
|
||||
HorizontalFadingEdgeBox(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.wrapContentHeight()
|
||||
.requiredHeight(30.dp)
|
||||
.padding(7.dp, 5.dp)
|
||||
.clipToBounds(),
|
||||
backgroundColor = FirefoxTheme.colors.layer2,
|
||||
isContentRtl = BidiFormatter.getInstance().isRtl(tab.content.title)
|
||||
) {
|
||||
Text(
|
||||
text = tab.content.title,
|
||||
fontSize = 14.sp,
|
||||
maxLines = 1,
|
||||
softWrap = false,
|
||||
style = TextStyle(
|
||||
color = FirefoxTheme.colors.textPrimary,
|
||||
textDirection = TextDirection.Content
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.mozac_ic_close),
|
||||
contentDescription = stringResource(id = R.string.close_tab),
|
||||
tint = FirefoxTheme.colors.iconPrimary,
|
||||
modifier = Modifier
|
||||
.clickable { onCloseClick(tab) }
|
||||
.size(24.dp)
|
||||
.align(Alignment.CenterVertically)
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
Divider(
|
||||
color = FirefoxTheme.colors.borderPrimary,
|
||||
thickness = 1.dp
|
||||
)
|
||||
|
||||
Thumbnail(
|
||||
tab = tab,
|
||||
multiSelectionSelected = multiSelectionSelected,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (!multiSelectionEnabled) {
|
||||
MediaImage(
|
||||
tab = tab,
|
||||
onMediaIconClicked = { onMediaClick(tab) },
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopStart)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thumbnail specific for the [TabGridItem], which can be selected.
|
||||
*
|
||||
* @param tab Tab, containing the thumbnail to be displayed.
|
||||
* @param multiSelectionSelected Whether or not the multiple selection is enabled.
|
||||
*/
|
||||
@Composable
|
||||
private fun Thumbnail(
|
||||
tab: TabSessionState,
|
||||
multiSelectionSelected: Boolean,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(FirefoxTheme.colors.layer2)
|
||||
) {
|
||||
GridTabThumbnail(
|
||||
tabId = tab.id,
|
||||
size = LocalConfiguration.current.screenWidthDp.dp
|
||||
)
|
||||
|
||||
if (multiSelectionSelected) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(FirefoxTheme.colors.layerAccentNonOpaque)
|
||||
)
|
||||
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.size(size = 40.dp)
|
||||
.align(alignment = Alignment.Center),
|
||||
shape = CircleShape,
|
||||
backgroundColor = FirefoxTheme.colors.layerAccent,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.mozac_ic_check),
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.padding(all = 8.dp),
|
||||
contentDescription = null,
|
||||
tint = colorResource(id = R.color.mozac_ui_icons_fill)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
private fun TabGridItemPreview() {
|
||||
FirefoxTheme {
|
||||
TabGridItem(
|
||||
tab = createTab(
|
||||
url = "www.mozilla.com",
|
||||
title = "Mozilla Domain"
|
||||
),
|
||||
onCloseClick = {},
|
||||
onMediaClick = {},
|
||||
onClick = {},
|
||||
onLongClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
private fun TabGridItemSelectedPreview() {
|
||||
FirefoxTheme {
|
||||
TabGridItem(
|
||||
tab = createTab(url = "www.mozilla.com", title = "Mozilla"),
|
||||
isSelected = true,
|
||||
onCloseClick = {},
|
||||
onMediaClick = {},
|
||||
onClick = {},
|
||||
onLongClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
private fun TabGridItemMultiSelectedPreview() {
|
||||
FirefoxTheme {
|
||||
TabGridItem(
|
||||
tab = createTab(url = "www.mozilla.com", title = "Mozilla"),
|
||||
multiSelectionEnabled = true,
|
||||
multiSelectionSelected = true,
|
||||
onCloseClick = {},
|
||||
onMediaClick = {},
|
||||
onClick = {},
|
||||
onLongClick = {},
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.tabstray.browser.compose
|
||||
|
||||
import android.view.View
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.browser.tabstray.TabsTray
|
||||
import mozilla.components.browser.tabstray.TabsTrayStyling
|
||||
import mozilla.components.lib.state.ext.observeAsComposableState
|
||||
import org.mozilla.fenix.compose.tabstray.TabGridItem
|
||||
import org.mozilla.fenix.selection.SelectionHolder
|
||||
import org.mozilla.fenix.tabstray.TabsTrayState
|
||||
import org.mozilla.fenix.tabstray.TabsTrayStore
|
||||
import org.mozilla.fenix.tabstray.browser.BrowserTrayInteractor
|
||||
|
||||
/**
|
||||
* A Compose ViewHolder implementation for "tab" items with grid layout.
|
||||
*
|
||||
* @param interactor [BrowserTrayInteractor] handling tabs interactions in a tab tray.
|
||||
* @param store [TabsTrayStore] containing the complete state of tabs tray and methods to update that.
|
||||
* @param selectionHolder [SelectionHolder]<[TabSessionState]> for helping with selecting
|
||||
* any number of displayed [TabSessionState]s.
|
||||
* @param composeItemView that displays a "tab".
|
||||
* @param featureName [String] representing the name of the feature displaying tabs. Used in telemetry reporting.
|
||||
* @param viewLifecycleOwner [LifecycleOwner] to which this Composable will be tied to.
|
||||
*/
|
||||
class ComposeGridViewHolder(
|
||||
private val interactor: BrowserTrayInteractor,
|
||||
private val store: TabsTrayStore,
|
||||
private val selectionHolder: SelectionHolder<TabSessionState>? = null,
|
||||
composeItemView: ComposeView,
|
||||
private val featureName: String,
|
||||
viewLifecycleOwner: LifecycleOwner,
|
||||
) : ComposeAbstractTabViewHolder(composeItemView, viewLifecycleOwner) {
|
||||
|
||||
override var tab: TabSessionState? = null
|
||||
private var isMultiSelectionSelectedState = MutableStateFlow(false)
|
||||
private var isSelectedTabState = MutableStateFlow(false)
|
||||
|
||||
override fun bind(
|
||||
tab: TabSessionState,
|
||||
isSelected: Boolean,
|
||||
styling: TabsTrayStyling,
|
||||
delegate: TabsTray.Delegate
|
||||
) {
|
||||
this.tab = tab
|
||||
isSelectedTabState.value = isSelected
|
||||
bind(tab)
|
||||
}
|
||||
|
||||
override fun updateSelectedTabIndicator(showAsSelected: Boolean) {
|
||||
isSelectedTabState.value = showAsSelected
|
||||
}
|
||||
|
||||
override fun showTabIsMultiSelectEnabled(selectedMaskView: View?, isSelected: Boolean) {
|
||||
isMultiSelectionSelectedState.value = isSelected
|
||||
}
|
||||
|
||||
private fun onCloseClicked(tab: TabSessionState) {
|
||||
interactor.onTabClosed(tab, featureName)
|
||||
}
|
||||
|
||||
private fun onClick(tab: TabSessionState) {
|
||||
val holder = selectionHolder
|
||||
if (holder != null) {
|
||||
interactor.onMultiSelectClicked(tab, holder, featureName)
|
||||
} else {
|
||||
interactor.onTabSelected(tab, featureName)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onLongClick(tab: TabSessionState) {
|
||||
val holder = selectionHolder ?: return
|
||||
interactor.onLongClicked(tab, holder)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content(tab: TabSessionState) {
|
||||
val multiSelectionEnabled = store.observeAsComposableState { state ->
|
||||
state.mode is TabsTrayState.Mode.Select
|
||||
}.value ?: false
|
||||
val isSelectedTab by isSelectedTabState.collectAsState()
|
||||
val isMultiSelectionSelected by isMultiSelectionSelectedState.collectAsState()
|
||||
|
||||
TabGridItem(
|
||||
tab = tab,
|
||||
isSelected = isSelectedTab,
|
||||
multiSelectionEnabled = multiSelectionEnabled,
|
||||
multiSelectionSelected = isMultiSelectionSelected,
|
||||
onCloseClick = ::onCloseClicked,
|
||||
onMediaClick = interactor::onMediaClicked,
|
||||
onClick = ::onClick,
|
||||
onLongClick = ::onLongClick,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val LAYOUT_ID = View.generateViewId()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue