[a11y] Let keyboard shortcut Shift + F10 trigger the same action than a long click
This commit is contained in:
@@ -46,6 +46,7 @@ import io.element.android.libraries.core.extensions.orEmpty
|
||||
import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAtom
|
||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarType
|
||||
import io.element.android.libraries.designsystem.modifiers.onShiftF10
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Button
|
||||
@@ -170,14 +171,15 @@ private fun RoomSummaryScaffoldRow(
|
||||
hideAvatarImage: Boolean = false,
|
||||
content: @Composable ColumnScope.() -> Unit
|
||||
) {
|
||||
val clickModifier = Modifier.combinedClickable(
|
||||
onClick = { onClick(room) },
|
||||
onLongClick = { onLongClick(room) },
|
||||
onLongClickLabel = stringResource(CommonStrings.action_open_context_menu),
|
||||
indication = ripple(),
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
)
|
||||
|
||||
val clickModifier = Modifier
|
||||
.combinedClickable(
|
||||
onClick = { onClick(room) },
|
||||
onLongClick = { onLongClick(room) },
|
||||
onLongClickLabel = stringResource(CommonStrings.action_open_context_menu),
|
||||
indication = ripple(),
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
)
|
||||
.onShiftF10 { onLongClick(room) }
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
|
||||
@@ -38,6 +38,7 @@ import io.element.android.features.messages.impl.timeline.model.bubble.BubbleSta
|
||||
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleStateProvider
|
||||
import io.element.android.libraries.core.extensions.to01
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.designsystem.modifiers.onShiftF10
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.text.toDp
|
||||
@@ -96,12 +97,14 @@ fun MessageEventBubble(
|
||||
val clickableModifier = if (isTalkbackActive()) {
|
||||
Modifier
|
||||
} else {
|
||||
Modifier.combinedClickable(
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
indication = ripple(),
|
||||
interactionSource = interactionSource
|
||||
)
|
||||
Modifier
|
||||
.combinedClickable(
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
indication = ripple(),
|
||||
interactionSource = interactionSource
|
||||
)
|
||||
.onShiftF10(onLongClick)
|
||||
}
|
||||
|
||||
// Ignore state.isHighlighted for now, we need a design decision on it.
|
||||
|
||||
@@ -19,6 +19,7 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.libraries.designsystem.modifiers.onShiftF10
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Surface
|
||||
@@ -46,7 +47,8 @@ fun MessageStateEventContainer(
|
||||
onLongClickLabel = stringResource(CommonStrings.action_open_context_menu),
|
||||
indication = ripple(),
|
||||
interactionSource = interactionSource
|
||||
),
|
||||
)
|
||||
.onShiftF10(onLongClick),
|
||||
color = backgroundColor,
|
||||
shape = shape,
|
||||
content = content
|
||||
|
||||
@@ -43,6 +43,7 @@ import io.element.android.features.messages.impl.timeline.model.AggregatedReacti
|
||||
import io.element.android.features.messages.impl.timeline.model.AggregatedReactionProvider
|
||||
import io.element.android.features.messages.impl.timeline.model.aTimelineItemReactions
|
||||
import io.element.android.libraries.designsystem.icons.CompoundDrawables
|
||||
import io.element.android.libraries.designsystem.modifiers.onShiftF10
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.text.toDp
|
||||
@@ -107,6 +108,7 @@ fun MessagesReactionButton(
|
||||
onLongClickLabel = stringResource(CommonStrings.action_open_context_menu),
|
||||
onLongClick = onLongClick
|
||||
)
|
||||
.onShiftF10(onLongClick)
|
||||
// Inner border, to highlight when selected
|
||||
.border(BorderStroke(1.dp, borderColor), RoundedCornerShape(corner = CornerSize(12.dp)))
|
||||
.background(buttonColor, RoundedCornerShape(corner = CornerSize(12.dp)))
|
||||
|
||||
@@ -34,6 +34,7 @@ import io.element.android.features.roomcall.api.RoomCallState
|
||||
import io.element.android.features.roomcall.api.RoomCallStateProvider
|
||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarType
|
||||
import io.element.android.libraries.designsystem.modifiers.onShiftF10
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.text.toDp
|
||||
@@ -57,6 +58,7 @@ internal fun TimelineItemCallNotifyView(
|
||||
onLongClick = { onLongClick(event) },
|
||||
onLongClickLabel = stringResource(CommonStrings.action_open_context_menu),
|
||||
)
|
||||
.onShiftF10 { onLongClick(event) }
|
||||
.padding(12.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
||||
@@ -37,6 +37,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
|
||||
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionEvent
|
||||
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState
|
||||
import io.element.android.libraries.designsystem.modifiers.onShiftF10
|
||||
import io.element.android.libraries.designsystem.modifiers.subtleColorStops
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
@@ -148,11 +149,13 @@ internal fun TimelineItemRow(
|
||||
// Custom clickable that applies over the whole item for accessibility
|
||||
.then(
|
||||
if (isTalkbackActive()) {
|
||||
Modifier.combinedClickable(
|
||||
onClick = { onContentClick(timelineItem) },
|
||||
onLongClick = { onLongClick(timelineItem) },
|
||||
onLongClickLabel = stringResource(CommonStrings.action_open_context_menu),
|
||||
)
|
||||
Modifier
|
||||
.combinedClickable(
|
||||
onClick = { onContentClick(timelineItem) },
|
||||
onLongClick = { onLongClick(timelineItem) },
|
||||
onLongClickLabel = stringResource(CommonStrings.action_open_context_menu),
|
||||
)
|
||||
.onShiftF10 { onLongClick(timelineItem) }
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI
|
||||
import io.element.android.features.messages.impl.timeline.protection.ProtectedView
|
||||
import io.element.android.features.messages.impl.timeline.protection.coerceRatioWhenHidingContent
|
||||
import io.element.android.libraries.designsystem.components.blurhash.blurHashBackground
|
||||
import io.element.android.libraries.designsystem.modifiers.onShiftF10
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.textcomposer.ElementRichTextEditorStyle
|
||||
@@ -91,10 +92,12 @@ fun TimelineItemImageView(
|
||||
.then(if (isLoaded) Modifier.background(Color.White) else Modifier)
|
||||
.then(
|
||||
if (!isTalkbackActive() && onContentClick != null) {
|
||||
Modifier.combinedClickable(
|
||||
onClick = onContentClick,
|
||||
onLongClick = onLongClick
|
||||
)
|
||||
Modifier
|
||||
.combinedClickable(
|
||||
onClick = onContentClick,
|
||||
onLongClick = onLongClick,
|
||||
)
|
||||
.onShiftF10(onLongClick)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||
import io.element.android.features.messages.impl.timeline.protection.ProtectedView
|
||||
import io.element.android.features.messages.impl.timeline.protection.coerceRatioWhenHidingContent
|
||||
import io.element.android.libraries.designsystem.components.blurhash.blurHashBackground
|
||||
import io.element.android.libraries.designsystem.modifiers.onShiftF10
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.matrix.ui.media.MediaRequestData
|
||||
@@ -74,6 +75,7 @@ fun TimelineItemStickerView(
|
||||
onLongClick = onLongClick,
|
||||
onLongClickLabel = stringResource(CommonStrings.action_open_context_menu),
|
||||
)
|
||||
.onShiftF10(onLongClick)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI
|
||||
import io.element.android.features.messages.impl.timeline.protection.ProtectedView
|
||||
import io.element.android.features.messages.impl.timeline.protection.coerceRatioWhenHidingContent
|
||||
import io.element.android.libraries.designsystem.components.blurhash.blurHashBackground
|
||||
import io.element.android.libraries.designsystem.modifiers.onShiftF10
|
||||
import io.element.android.libraries.designsystem.modifiers.roundedBackground
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
@@ -105,10 +106,12 @@ fun TimelineItemVideoView(
|
||||
.then(if (isLoaded) Modifier.background(Color.White) else Modifier)
|
||||
.then(
|
||||
if (!isTalkbackActive && onContentClick != null) {
|
||||
Modifier.combinedClickable(
|
||||
onClick = onContentClick,
|
||||
onLongClick = onLongClick
|
||||
)
|
||||
Modifier
|
||||
.combinedClickable(
|
||||
onClick = onContentClick,
|
||||
onLongClick = onLongClick,
|
||||
)
|
||||
.onShiftF10(onLongClick)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.modifiers
|
||||
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.input.key.KeyEventType
|
||||
import androidx.compose.ui.input.key.isShiftPressed
|
||||
import androidx.compose.ui.input.key.key
|
||||
import androidx.compose.ui.input.key.onKeyEvent
|
||||
import androidx.compose.ui.input.key.type
|
||||
|
||||
/**
|
||||
* Modifier to handle Shift + F10 key events.
|
||||
* This is typically used to trigger context menus in desktop applications.
|
||||
*
|
||||
* @param onShiftF10Press The callback to invoke when Shift + F10 is pressed.
|
||||
*/
|
||||
fun Modifier.onShiftF10(
|
||||
onShiftF10Press: (() -> Unit)?,
|
||||
): Modifier = then(
|
||||
if (onShiftF10Press == null) {
|
||||
Modifier
|
||||
} else {
|
||||
Modifier.onKeyEvent { keyEvent ->
|
||||
// invoke the callback when the user presses Shift + F10
|
||||
if (keyEvent.type == KeyEventType.KeyUp &&
|
||||
keyEvent.isShiftPressed &&
|
||||
keyEvent.key == Key.F10) {
|
||||
onShiftF10Press()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -30,6 +30,7 @@ import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.core.extensions.withBrackets
|
||||
import io.element.android.libraries.designsystem.modifiers.onShiftF10
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
|
||||
@@ -84,6 +85,7 @@ private fun FilenameRow(
|
||||
onLongClick = onLongClick,
|
||||
onLongClickLabel = stringResource(CommonStrings.action_open_context_menu),
|
||||
)
|
||||
.onShiftF10(onLongClick)
|
||||
.fillMaxWidth()
|
||||
.padding(start = 12.dp, end = 36.dp, top = 8.dp, bottom = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
||||
@@ -30,6 +30,7 @@ import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.core.extensions.withBrackets
|
||||
import io.element.android.libraries.designsystem.modifiers.onShiftF10
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
|
||||
@@ -84,6 +85,7 @@ private fun FilenameRow(
|
||||
onLongClick = onLongClick,
|
||||
onLongClickLabel = stringResource(CommonStrings.action_open_context_menu),
|
||||
)
|
||||
.onShiftF10(onLongClick)
|
||||
.fillMaxWidth()
|
||||
.padding(start = 12.dp, end = 36.dp, top = 8.dp, bottom = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
||||
@@ -24,6 +24,7 @@ import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import coil3.compose.AsyncImage
|
||||
import coil3.compose.AsyncImagePainter
|
||||
import io.element.android.libraries.designsystem.modifiers.onShiftF10
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.mediaviewer.impl.model.MediaItem
|
||||
@@ -44,7 +45,8 @@ fun ImageItemView(
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
onLongClickLabel = stringResource(CommonStrings.action_open_context_menu),
|
||||
),
|
||||
)
|
||||
.onShiftF10(onLongClick),
|
||||
) {
|
||||
var isLoaded by remember { mutableStateOf(false) }
|
||||
AsyncImage(
|
||||
|
||||
@@ -33,6 +33,7 @@ import coil3.compose.AsyncImage
|
||||
import coil3.compose.AsyncImagePainter
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.designsystem.modifiers.onShiftF10
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
@@ -54,7 +55,8 @@ fun VideoItemView(
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
onLongClickLabel = stringResource(CommonStrings.action_open_context_menu),
|
||||
),
|
||||
)
|
||||
.onShiftF10(onLongClick),
|
||||
) {
|
||||
var isLoaded by remember { mutableStateOf(false) }
|
||||
AsyncImage(
|
||||
|
||||
@@ -38,6 +38,7 @@ import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.designsystem.components.media.WaveformPlaybackView
|
||||
import io.element.android.libraries.designsystem.modifiers.onShiftF10
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
||||
@@ -105,6 +106,7 @@ private fun VoiceInfoRow(
|
||||
onLongClick = onLongClick,
|
||||
onLongClickLabel = stringResource(CommonStrings.action_open_context_menu),
|
||||
)
|
||||
.onShiftF10(onLongClick)
|
||||
.fillMaxWidth()
|
||||
.padding(start = 12.dp, end = 36.dp, top = 8.dp, bottom = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
||||
Reference in New Issue
Block a user