AvatarPickerView: ensure the pencil icon always has the same size and improve the preview.
This commit is contained in:
parent
3b078654a2
commit
af2bf8435b
1 changed files with 65 additions and 111 deletions
|
|
@ -61,8 +61,16 @@ import io.element.android.libraries.testtags.TestTags
|
||||||
import io.element.android.libraries.testtags.testTag
|
import io.element.android.libraries.testtags.testTag
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
|
private val editIconContainerSize = 30.dp
|
||||||
|
private val editIconContainerRadius = editIconContainerSize / 2
|
||||||
|
private val editIconContainerPadding = 4.dp
|
||||||
|
private val editIconSize = 20.dp
|
||||||
|
private val editIconOffset = 8.dp
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Avatar picker view, based on https://www.figma.com/design/kcnHxunG1LDWXsJhaNuiHz/ER-145--Spaces-on-Element-X?node-id=5918-97417&t=JYDQysgjS33AZb74-4
|
* Avatar picker view.
|
||||||
|
*
|
||||||
|
* https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=1949-1384
|
||||||
*
|
*
|
||||||
* It takes a [state], which can be [AvatarPickerState.Pick] for displaying the 'pick avatar' button, or [AvatarPickerState.Selected] when an avatar has
|
* It takes a [state], which can be [AvatarPickerState.Pick] for displaying the 'pick avatar' button, or [AvatarPickerState.Selected] when an avatar has
|
||||||
* already been selected.
|
* already been selected.
|
||||||
|
|
@ -96,7 +104,6 @@ fun AvatarPickerView(
|
||||||
|
|
||||||
fun eraseBackgroundModifier(
|
fun eraseBackgroundModifier(
|
||||||
parentWidth: Dp,
|
parentWidth: Dp,
|
||||||
editIconRadius: Dp,
|
|
||||||
) = Modifier
|
) = Modifier
|
||||||
.graphicsLayer {
|
.graphicsLayer {
|
||||||
compositingStrategy = CompositingStrategy.Offscreen
|
compositingStrategy = CompositingStrategy.Offscreen
|
||||||
|
|
@ -107,13 +114,13 @@ fun AvatarPickerView(
|
||||||
color = Color.Black,
|
color = Color.Black,
|
||||||
center = Offset(
|
center = Offset(
|
||||||
x = if (layoutDirection == LayoutDirection.Ltr) {
|
x = if (layoutDirection == LayoutDirection.Ltr) {
|
||||||
parentWidth.toPx() - editIconRadius.toPx() * 0.48f
|
(parentWidth - editIconContainerRadius + editIconOffset).toPx()
|
||||||
} else {
|
} else {
|
||||||
editIconRadius.toPx() * 0.48f
|
(editIconContainerRadius - editIconOffset).toPx()
|
||||||
},
|
},
|
||||||
y = size.height - editIconRadius.toPx(),
|
y = size.height - editIconContainerRadius.toPx(),
|
||||||
),
|
),
|
||||||
radius = editIconRadius.toPx() * 1.35f,
|
radius = (editIconContainerRadius + editIconContainerPadding).toPx(),
|
||||||
blendMode = BlendMode.Clear,
|
blendMode = BlendMode.Clear,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +139,7 @@ fun AvatarPickerView(
|
||||||
is AvatarPickerState.Selected -> {
|
is AvatarPickerState.Selected -> {
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
val backgroundModifier = if (enabled) {
|
val backgroundModifier = if (enabled) {
|
||||||
eraseBackgroundModifier(state.avatarData.size.dp, state.avatarData.size.dp * 0.225f)
|
eraseBackgroundModifier(state.avatarData.size.dp)
|
||||||
} else {
|
} else {
|
||||||
Modifier
|
Modifier
|
||||||
}
|
}
|
||||||
|
|
@ -143,7 +150,6 @@ fun AvatarPickerView(
|
||||||
)
|
)
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
OverlayEditButton(
|
OverlayEditButton(
|
||||||
editButtonSize = state.avatarData.size.dp * 30 / 64f,
|
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
interactionSource = interactionSource
|
interactionSource = interactionSource
|
||||||
)
|
)
|
||||||
|
|
@ -179,15 +185,14 @@ private fun PickButton(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun BoxScope.OverlayEditButton(
|
private fun BoxScope.OverlayEditButton(
|
||||||
editButtonSize: Dp,
|
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
interactionSource: MutableInteractionSource
|
interactionSource: MutableInteractionSource
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.BottomEnd)
|
.align(Alignment.BottomEnd)
|
||||||
.size(editButtonSize)
|
.size(editIconContainerSize)
|
||||||
.offset(x = editButtonSize * 0.266f)
|
.offset(x = editIconOffset)
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
.clickable(interactionSource = interactionSource, onClick = onClick, indication = null)
|
.clickable(interactionSource = interactionSource, onClick = onClick, indication = null)
|
||||||
.background(ElementTheme.colors.bgCanvasDefault)
|
.background(ElementTheme.colors.bgCanvasDefault)
|
||||||
|
|
@ -195,7 +200,7 @@ private fun BoxScope.OverlayEditButton(
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
modifier = Modifier.size(editButtonSize * 20 / 30f),
|
modifier = Modifier.size(editIconSize),
|
||||||
imageVector = CompoundIcons.Edit(),
|
imageVector = CompoundIcons.Edit(),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
)
|
)
|
||||||
|
|
@ -234,97 +239,45 @@ internal fun AvatarPickerViewRtlPreview() = CompositionLocalProvider(
|
||||||
@PreviewsDayNight
|
@PreviewsDayNight
|
||||||
@Composable
|
@Composable
|
||||||
internal fun AvatarPickerSizesPreview() = ElementPreview {
|
internal fun AvatarPickerSizesPreview() = ElementPreview {
|
||||||
Column {
|
// Size used across the codebase
|
||||||
Row {
|
val sizes = listOf(
|
||||||
AvatarPickerView(AvatarPickerState.Pick(buttonSize = 24.dp, externalPadding = PaddingValues(6.dp)), onClick = {})
|
AvatarSize.EditRoomDetails,
|
||||||
AvatarPickerView(AvatarPickerState.Pick(buttonSize = 32.dp, externalPadding = PaddingValues(6.dp)), onClick = {})
|
AvatarSize.EditProfileDetails,
|
||||||
AvatarPickerView(AvatarPickerState.Pick(buttonSize = 48.dp, externalPadding = PaddingValues(6.dp)), onClick = {})
|
)
|
||||||
AvatarPickerView(AvatarPickerState.Pick(buttonSize = 64.dp, externalPadding = PaddingValues(6.dp)), onClick = {})
|
Column(
|
||||||
AvatarPickerView(AvatarPickerState.Pick(buttonSize = 96.dp, externalPadding = PaddingValues(6.dp)), onClick = {})
|
modifier = Modifier.padding(12.dp)
|
||||||
|
) {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
sizes.forEach {
|
||||||
|
AvatarPickerView(
|
||||||
|
state = AvatarPickerState.Pick(buttonSize = it.dp, externalPadding = PaddingValues(6.dp)),
|
||||||
|
onClick = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Row {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
AvatarPickerView(
|
sizes.forEach {
|
||||||
AvatarPickerState.Selected(
|
AvatarPickerView(
|
||||||
avatarData = AvatarData("@user:example.com", "User", "content://test", size = AvatarSize.TimelineThreadLatestEventSender),
|
AvatarPickerState.Selected(
|
||||||
type = AvatarType.User
|
avatarData = AvatarData("@user:example.com", "User", "content://test", size = it),
|
||||||
),
|
type = AvatarType.User,
|
||||||
onClick = {},
|
),
|
||||||
modifier = Modifier.padding(6.dp)
|
onClick = {},
|
||||||
)
|
modifier = Modifier.padding(6.dp)
|
||||||
AvatarPickerView(
|
)
|
||||||
AvatarPickerState.Selected(
|
}
|
||||||
avatarData = AvatarData("@user:example.com", "User", "content://test", size = AvatarSize.ReadReceiptList),
|
|
||||||
type = AvatarType.User
|
|
||||||
),
|
|
||||||
onClick = {},
|
|
||||||
modifier = Modifier.padding(6.dp)
|
|
||||||
)
|
|
||||||
AvatarPickerView(
|
|
||||||
AvatarPickerState.Selected(
|
|
||||||
avatarData = AvatarData("@user:example.com", "User", "content://test", size = AvatarSize.SelectedUser),
|
|
||||||
type = AvatarType.User
|
|
||||||
),
|
|
||||||
onClick = {},
|
|
||||||
modifier = Modifier.padding(6.dp)
|
|
||||||
)
|
|
||||||
AvatarPickerView(
|
|
||||||
AvatarPickerState.Selected(
|
|
||||||
avatarData = AvatarData("@user:example.com", "User", "content://test", size = AvatarSize.EditRoomDetails),
|
|
||||||
type = AvatarType.User
|
|
||||||
),
|
|
||||||
onClick = {},
|
|
||||||
modifier = Modifier.padding(6.dp)
|
|
||||||
)
|
|
||||||
AvatarPickerView(
|
|
||||||
AvatarPickerState.Selected(
|
|
||||||
avatarData = AvatarData("@user:example.com", "User", "content://test", size = AvatarSize.RoomListManageUser),
|
|
||||||
type = AvatarType.User
|
|
||||||
),
|
|
||||||
onClick = {},
|
|
||||||
modifier = Modifier.padding(6.dp)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Row {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
AvatarPickerView(
|
sizes.forEach {
|
||||||
AvatarPickerState.Selected(
|
AvatarPickerView(
|
||||||
avatarData = AvatarData("@user:example.com", "User", "content://test", size = AvatarSize.TimelineThreadLatestEventSender),
|
AvatarPickerState.Selected(
|
||||||
type = AvatarType.Space()
|
avatarData = AvatarData("@user:example.com", "User", "content://test", size = it),
|
||||||
),
|
type = AvatarType.Space(),
|
||||||
onClick = {},
|
),
|
||||||
modifier = Modifier.padding(6.dp)
|
onClick = {},
|
||||||
)
|
modifier = Modifier.padding(6.dp)
|
||||||
AvatarPickerView(
|
)
|
||||||
AvatarPickerState.Selected(
|
}
|
||||||
avatarData = AvatarData("@user:example.com", "User", "content://test", size = AvatarSize.ReadReceiptList),
|
|
||||||
type = AvatarType.Space()
|
|
||||||
),
|
|
||||||
onClick = {},
|
|
||||||
modifier = Modifier.padding(6.dp)
|
|
||||||
)
|
|
||||||
AvatarPickerView(
|
|
||||||
AvatarPickerState.Selected(
|
|
||||||
avatarData = AvatarData("@user:example.com", "User", "content://test", size = AvatarSize.SelectedUser),
|
|
||||||
type = AvatarType.Space()
|
|
||||||
),
|
|
||||||
onClick = {},
|
|
||||||
modifier = Modifier.padding(6.dp)
|
|
||||||
)
|
|
||||||
AvatarPickerView(
|
|
||||||
AvatarPickerState.Selected(
|
|
||||||
avatarData = AvatarData("@user:example.com", "User", "content://test", size = AvatarSize.EditRoomDetails),
|
|
||||||
type = AvatarType.Space()
|
|
||||||
),
|
|
||||||
onClick = {},
|
|
||||||
modifier = Modifier.padding(6.dp)
|
|
||||||
)
|
|
||||||
AvatarPickerView(
|
|
||||||
AvatarPickerState.Selected(
|
|
||||||
avatarData = AvatarData("@user:example.com", "User", "content://test", size = AvatarSize.RoomListManageUser),
|
|
||||||
type = AvatarType.Space()
|
|
||||||
),
|
|
||||||
onClick = {},
|
|
||||||
modifier = Modifier.padding(6.dp)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -335,8 +288,9 @@ private fun PreviewContent() {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
|
val size = AvatarSize.EditRoomDetails
|
||||||
Text("Pick image")
|
Text("Pick image")
|
||||||
AvatarPickerView(AvatarPickerState.Pick(buttonSize = 48.dp, externalPadding = PaddingValues(6.dp)), onClick = {})
|
AvatarPickerView(AvatarPickerState.Pick(buttonSize = size.dp, externalPadding = PaddingValues(6.dp)), onClick = {})
|
||||||
HorizontalDivider()
|
HorizontalDivider()
|
||||||
|
|
||||||
Text("User avatar")
|
Text("User avatar")
|
||||||
|
|
@ -345,7 +299,7 @@ private fun PreviewContent() {
|
||||||
Text("No url")
|
Text("No url")
|
||||||
AvatarPickerView(
|
AvatarPickerView(
|
||||||
AvatarPickerState.Selected(
|
AvatarPickerState.Selected(
|
||||||
avatarData = AvatarData("@user:example.com", "User", null, size = AvatarSize.EditRoomDetails),
|
avatarData = AvatarData("@user:example.com", "User", null, size = size),
|
||||||
type = AvatarType.User
|
type = AvatarType.User
|
||||||
),
|
),
|
||||||
onClick = {},
|
onClick = {},
|
||||||
|
|
@ -356,7 +310,7 @@ private fun PreviewContent() {
|
||||||
Text("Local")
|
Text("Local")
|
||||||
AvatarPickerView(
|
AvatarPickerView(
|
||||||
AvatarPickerState.Selected(
|
AvatarPickerState.Selected(
|
||||||
avatarData = AvatarData("@user:example.com", "User", "content://test", size = AvatarSize.EditRoomDetails),
|
avatarData = AvatarData("@user:example.com", "User", "content://test", size = size),
|
||||||
type = AvatarType.User
|
type = AvatarType.User
|
||||||
),
|
),
|
||||||
onClick = {},
|
onClick = {},
|
||||||
|
|
@ -367,7 +321,7 @@ private fun PreviewContent() {
|
||||||
Text("MXC")
|
Text("MXC")
|
||||||
AvatarPickerView(
|
AvatarPickerView(
|
||||||
AvatarPickerState.Selected(
|
AvatarPickerState.Selected(
|
||||||
avatarData = AvatarData("@user:example.com", "User", "mxc://test", size = AvatarSize.EditRoomDetails),
|
avatarData = AvatarData("@user:example.com", "User", "mxc://test", size = size),
|
||||||
type = AvatarType.User
|
type = AvatarType.User
|
||||||
),
|
),
|
||||||
onClick = {},
|
onClick = {},
|
||||||
|
|
@ -383,7 +337,7 @@ private fun PreviewContent() {
|
||||||
Text("No url")
|
Text("No url")
|
||||||
AvatarPickerView(
|
AvatarPickerView(
|
||||||
AvatarPickerState.Selected(
|
AvatarPickerState.Selected(
|
||||||
avatarData = AvatarData("!room:example.com", "Room", null, size = AvatarSize.EditRoomDetails),
|
avatarData = AvatarData("!room:example.com", "Room", null, size = size),
|
||||||
type = AvatarType.Room()
|
type = AvatarType.Room()
|
||||||
),
|
),
|
||||||
onClick = {},
|
onClick = {},
|
||||||
|
|
@ -394,7 +348,7 @@ private fun PreviewContent() {
|
||||||
Text("Local")
|
Text("Local")
|
||||||
AvatarPickerView(
|
AvatarPickerView(
|
||||||
AvatarPickerState.Selected(
|
AvatarPickerState.Selected(
|
||||||
avatarData = AvatarData("!room:example.com", "Room", "content://test", size = AvatarSize.EditRoomDetails),
|
avatarData = AvatarData("!room:example.com", "Room", "content://test", size = size),
|
||||||
type = AvatarType.Room()
|
type = AvatarType.Room()
|
||||||
),
|
),
|
||||||
onClick = {},
|
onClick = {},
|
||||||
|
|
@ -405,7 +359,7 @@ private fun PreviewContent() {
|
||||||
Text("MXC")
|
Text("MXC")
|
||||||
AvatarPickerView(
|
AvatarPickerView(
|
||||||
AvatarPickerState.Selected(
|
AvatarPickerState.Selected(
|
||||||
avatarData = AvatarData("!room:example.com", "Room", "mxc://test", size = AvatarSize.EditRoomDetails),
|
avatarData = AvatarData("!room:example.com", "Room", "mxc://test", size = size),
|
||||||
type = AvatarType.Room()
|
type = AvatarType.Room()
|
||||||
),
|
),
|
||||||
onClick = {},
|
onClick = {},
|
||||||
|
|
@ -421,7 +375,7 @@ private fun PreviewContent() {
|
||||||
Text("No url")
|
Text("No url")
|
||||||
AvatarPickerView(
|
AvatarPickerView(
|
||||||
AvatarPickerState.Selected(
|
AvatarPickerState.Selected(
|
||||||
avatarData = AvatarData("!room:example.com", "Space", null, size = AvatarSize.EditRoomDetails),
|
avatarData = AvatarData("!room:example.com", "Space", null, size = size),
|
||||||
type = AvatarType.Space()
|
type = AvatarType.Space()
|
||||||
),
|
),
|
||||||
onClick = {},
|
onClick = {},
|
||||||
|
|
@ -432,7 +386,7 @@ private fun PreviewContent() {
|
||||||
Text("Local")
|
Text("Local")
|
||||||
AvatarPickerView(
|
AvatarPickerView(
|
||||||
AvatarPickerState.Selected(
|
AvatarPickerState.Selected(
|
||||||
avatarData = AvatarData("!room:example.com", "Space", "content://test", size = AvatarSize.EditRoomDetails),
|
avatarData = AvatarData("!room:example.com", "Space", "content://test", size = size),
|
||||||
type = AvatarType.Space()
|
type = AvatarType.Space()
|
||||||
),
|
),
|
||||||
onClick = {},
|
onClick = {},
|
||||||
|
|
@ -443,7 +397,7 @@ private fun PreviewContent() {
|
||||||
Text("MXC")
|
Text("MXC")
|
||||||
AvatarPickerView(
|
AvatarPickerView(
|
||||||
AvatarPickerState.Selected(
|
AvatarPickerState.Selected(
|
||||||
avatarData = AvatarData("!room:example.com", "Space", "mxc://test", size = AvatarSize.EditRoomDetails),
|
avatarData = AvatarData("!room:example.com", "Space", "mxc://test", size = size),
|
||||||
type = AvatarType.Space()
|
type = AvatarType.Space()
|
||||||
),
|
),
|
||||||
onClick = {},
|
onClick = {},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue