From 5bf4e4c8ce851ce077ed73a3e46b10c1ffa7436b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Aug 2025 16:29:50 +0200 Subject: [PATCH 1/9] [a11y] Ensure external keyboard `Esc` key closes any bottom sheet. --- .../theme/components/ModalBottomSheet.kt | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt index 51a6cd9ee1..b362c729dc 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt @@ -23,6 +23,11 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.KeyEventType +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.key.onKeyEvent +import androidx.compose.ui.input.key.type import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp @@ -54,7 +59,17 @@ fun ModalBottomSheet( val safeSheetState = if (LocalInspectionMode.current) sheetStateForPreview() else sheetState androidx.compose.material3.ModalBottomSheet( onDismissRequest = onDismissRequest, - modifier = modifier, + modifier = modifier.onKeyEvent { keyEvent -> + // It seems that on some devices, we have to handle the Escape key manually to close the bottom sheet. + // This is not the case using an emulator, but is necessary on some physical devices. + if (keyEvent.type == KeyEventType.KeyUp && + keyEvent.key == Key.Escape) { + onDismissRequest() + true + } else { + false + } + }, sheetState = safeSheetState, shape = shape, containerColor = containerColor, From c2949b4753d0b45b9df73739d22985ad0186fac6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Aug 2025 16:36:21 +0200 Subject: [PATCH 2/9] Cleanup: `rememberModalBottomSheetState` is the default value for `sheetState` --- .../components/reactionsummary/ReactionSummaryView.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt index 081d93465f..d3dc63c031 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt @@ -32,7 +32,6 @@ import androidx.compose.foundation.shape.CornerSize import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf @@ -83,8 +82,6 @@ fun ReactionSummaryView( state: ReactionSummaryState, modifier: Modifier = Modifier, ) { - val sheetState = rememberModalBottomSheetState() - fun onDismiss() { state.eventSink(ReactionSummaryEvents.Clear) } @@ -92,7 +89,6 @@ fun ReactionSummaryView( if (state.target != null) { ModalBottomSheet( onDismissRequest = ::onDismiss, - sheetState = sheetState, modifier = modifier ) { ReactionSummaryViewContent(summary = state.target) From 3a97bff95a1a37bf70aeb5dbd4aa6407dffbf626 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Aug 2025 16:37:43 +0200 Subject: [PATCH 3/9] Cleanup: The local inspection mode is handled in `ModalBottomSheet`. --- .../impl/rolesandpermissions/RolesAndPermissionsView.kt | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsView.kt index 536b0f8b18..b581554a92 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsView.kt @@ -15,7 +15,6 @@ import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp @@ -31,7 +30,6 @@ import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.preferences.PreferencePage import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.preview.sheetStateForPreview import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.ListItem @@ -145,11 +143,7 @@ private fun ChangeOwnRoleBottomSheet( eventSink: (RolesAndPermissionsEvents) -> Unit, ) { val coroutineScope = rememberCoroutineScope() - val sheetState = if (LocalInspectionMode.current) { - sheetStateForPreview() - } else { - rememberModalBottomSheetState(skipPartiallyExpanded = true) - } + val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) fun dismiss() { sheetState.hide(coroutineScope) { eventSink(RolesAndPermissionsEvents.CancelPendingAction) From c4e654ac424b1e97893dce9f937306e8f49a09fc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 5 Aug 2025 09:17:06 +0200 Subject: [PATCH 4/9] Cleanup: Remove unused class --- .../configureroom/ConfigureRoomPresenterArgs.kt | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterArgs.kt diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterArgs.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterArgs.kt deleted file mode 100644 index b41b98b8cc..0000000000 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterArgs.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2023, 2024 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.features.createroom.impl.configureroom - -import io.element.android.libraries.matrix.api.user.MatrixUser - -data class ConfigureRoomPresenterArgs( - val selectedUsers: List, -) From eb33c8d864a96b04e8db5e88afc1d1846b7459b3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 5 Aug 2025 09:41:08 +0200 Subject: [PATCH 5/9] [a11y] Let keyboard shortcut Shift + F10 trigger the same action than a long click --- .../home/impl/components/RoomSummaryRow.kt | 18 ++++---- .../timeline/components/MessageEventBubble.kt | 15 ++++--- .../components/MessageStateEventContainer.kt | 4 +- .../components/MessagesReactionButton.kt | 2 + .../components/TimelineItemCallNotifyView.kt | 2 + .../timeline/components/TimelineItemRow.kt | 13 +++--- .../components/event/TimelineItemImageView.kt | 11 +++-- .../event/TimelineItemStickerView.kt | 2 + .../components/event/TimelineItemVideoView.kt | 11 +++-- .../designsystem/modifiers/Keyboard.kt | 42 +++++++++++++++++++ .../impl/gallery/ui/AudioItemView.kt | 2 + .../impl/gallery/ui/FileItemView.kt | 2 + .../impl/gallery/ui/ImageItemView.kt | 4 +- .../impl/gallery/ui/VideoItemView.kt | 4 +- .../impl/gallery/ui/VoiceItemView.kt | 2 + 15 files changed, 104 insertions(+), 30 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/Keyboard.kt diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt index b37e9ea223..1786c1139e 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt @@ -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() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt index 00069e9f82..710428bfab 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt @@ -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. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt index c988679626..70abb82278 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt @@ -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 diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt index 8c2a1d9ff6..86005640c9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt @@ -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))) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemCallNotifyView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemCallNotifyView.kt index ca34184491..579b825796 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemCallNotifyView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemCallNotifyView.kt @@ -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, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt index 59ec63d842..8753a90c42 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt @@ -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 } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt index 41d3873a65..94e71afeb2 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt @@ -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 } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt index dc7cfb2d52..79e4583ba4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt @@ -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 } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt index 18a59325d5..433d75dbc9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt @@ -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 } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/Keyboard.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/Keyboard.kt new file mode 100644 index 0000000000..c1a6a57428 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/Keyboard.kt @@ -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 + } + } + } +) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/AudioItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/AudioItemView.kt index c8d735d485..f0410981b8 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/AudioItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/AudioItemView.kt @@ -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, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/FileItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/FileItemView.kt index 1144fc3eaf..bcc19212f4 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/FileItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/FileItemView.kt @@ -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, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/ImageItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/ImageItemView.kt index 917c60df9e..f03f5e0955 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/ImageItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/ImageItemView.kt @@ -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( diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VideoItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VideoItemView.kt index cdc66c4eae..6a47567d76 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VideoItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VideoItemView.kt @@ -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( diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt index 5d84b79f8f..032f486cf7 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt @@ -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, From a7ea4d01be3c8248a20ad90f3e36a27653b04623 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 5 Aug 2025 12:00:12 +0200 Subject: [PATCH 6/9] Ensure that navigation using keyboard is not broken on the room list. Workaround https://issuetracker.google.com/issues/436432313 --- .../io/element/android/features/home/impl/HomeView.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt index 30a5c86243..cf1bede289 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt @@ -240,14 +240,19 @@ private fun HomeScaffold( contentPadding = PaddingValues( // FAB height is 56dp, bottom padding is 16dp, we add 8dp as extra margin -> 56+16+8 = 80, // and include provided bottom padding - bottom = 80.dp + padding.calculateBottomPadding(), - top = padding.calculateTopPadding() + // Disable contentPadding due to navigation issue using the keyboard + // See https://issuetracker.google.com/issues/436432313 + bottom = 80.dp, // + padding.calculateBottomPadding(), + //top = padding.calculateTopPadding() ), modifier = Modifier .padding( PaddingValues( start = padding.calculateStartPadding(LocalLayoutDirection.current), end = padding.calculateEndPadding(LocalLayoutDirection.current), + // Remove these two lines once https://issuetracker.google.com/issues/436432313 has been fixed + bottom = padding.calculateBottomPadding(), + top = padding.calculateTopPadding() ) ) .consumeWindowInsets(padding) From 81ed07a641e4099cee03f016742cb875a8fafda9 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 5 Aug 2025 10:11:31 +0000 Subject: [PATCH 7/9] Update screenshots --- .../snapshots/images/features.home.impl_HomeView_Day_3_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_3_en.png | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_3_en.png index 654b904236..3a0271ad4f 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eb923fffd20dc775f31b998e23c39acf79013a831a7c292c4cdba5b9142b4094 -size 68227 +oid sha256:6fd52151e94328a68ef5b65bdc0e5e0d730bd617ed2b58ad92c350fb409c75d2 +size 63096 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_3_en.png index 6324eecb45..4da8004df3 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9066615bfa2f78b77784302ce0272e3a486c79e267629c50c35ec1221c58046 -size 64443 +oid sha256:a67035a18b067b07f52090ee0ca9e3defa34a158e7dcefc55062131ac1599823 +size 59193 From 683628e352f98162daa81ade596a536fd3c2627e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 5 Aug 2025 21:07:10 +0200 Subject: [PATCH 8/9] Rename extension. --- .../features/home/impl/components/RoomSummaryRow.kt | 4 ++-- .../impl/timeline/components/MessageEventBubble.kt | 4 ++-- .../timeline/components/MessageStateEventContainer.kt | 4 ++-- .../impl/timeline/components/MessagesReactionButton.kt | 4 ++-- .../timeline/components/TimelineItemCallNotifyView.kt | 4 ++-- .../impl/timeline/components/TimelineItemRow.kt | 4 ++-- .../timeline/components/event/TimelineItemImageView.kt | 4 ++-- .../components/event/TimelineItemStickerView.kt | 4 ++-- .../timeline/components/event/TimelineItemVideoView.kt | 4 ++-- .../libraries/designsystem/modifiers/Keyboard.kt | 10 +++++----- .../mediaviewer/impl/gallery/ui/AudioItemView.kt | 4 ++-- .../mediaviewer/impl/gallery/ui/FileItemView.kt | 4 ++-- .../mediaviewer/impl/gallery/ui/ImageItemView.kt | 4 ++-- .../mediaviewer/impl/gallery/ui/VideoItemView.kt | 4 ++-- .../mediaviewer/impl/gallery/ui/VoiceItemView.kt | 4 ++-- 15 files changed, 33 insertions(+), 33 deletions(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt index 1786c1139e..a065da16e5 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt @@ -46,7 +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.modifiers.onKeyboardContextMenuAction 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 @@ -179,7 +179,7 @@ private fun RoomSummaryScaffoldRow( indication = ripple(), interactionSource = remember { MutableInteractionSource() } ) - .onShiftF10 { onLongClick(room) } + .onKeyboardContextMenuAction { onLongClick(room) } Row( modifier = modifier .fillMaxWidth() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt index 710428bfab..04fe0cb481 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt @@ -38,7 +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.modifiers.onKeyboardContextMenuAction import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.text.toDp @@ -104,7 +104,7 @@ fun MessageEventBubble( indication = ripple(), interactionSource = interactionSource ) - .onShiftF10(onLongClick) + .onKeyboardContextMenuAction(onLongClick) } // Ignore state.isHighlighted for now, we need a design decision on it. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt index 70abb82278..82eeb77177 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt @@ -19,7 +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.modifiers.onKeyboardContextMenuAction 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 @@ -48,7 +48,7 @@ fun MessageStateEventContainer( indication = ripple(), interactionSource = interactionSource ) - .onShiftF10(onLongClick), + .onKeyboardContextMenuAction(onLongClick), color = backgroundColor, shape = shape, content = content diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt index 86005640c9..c2673c0d84 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt @@ -43,7 +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.modifiers.onKeyboardContextMenuAction import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.text.toDp @@ -108,7 +108,7 @@ fun MessagesReactionButton( onLongClickLabel = stringResource(CommonStrings.action_open_context_menu), onLongClick = onLongClick ) - .onShiftF10(onLongClick) + .onKeyboardContextMenuAction(onLongClick) // Inner border, to highlight when selected .border(BorderStroke(1.dp, borderColor), RoundedCornerShape(corner = CornerSize(12.dp))) .background(buttonColor, RoundedCornerShape(corner = CornerSize(12.dp))) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemCallNotifyView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemCallNotifyView.kt index 579b825796..6cbd22c3fa 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemCallNotifyView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemCallNotifyView.kt @@ -34,7 +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.modifiers.onKeyboardContextMenuAction import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.text.toDp @@ -58,7 +58,7 @@ internal fun TimelineItemCallNotifyView( onLongClick = { onLongClick(event) }, onLongClickLabel = stringResource(CommonStrings.action_open_context_menu), ) - .onShiftF10 { onLongClick(event) } + .onKeyboardContextMenuAction { onLongClick(event) } .padding(12.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt index 8753a90c42..ff318b3f7e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt @@ -37,7 +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.onKeyboardContextMenuAction import io.element.android.libraries.designsystem.modifiers.subtleColorStops import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -155,7 +155,7 @@ internal fun TimelineItemRow( onLongClick = { onLongClick(timelineItem) }, onLongClickLabel = stringResource(CommonStrings.action_open_context_menu), ) - .onShiftF10 { onLongClick(timelineItem) } + .onKeyboardContextMenuAction { onLongClick(timelineItem) } } else { Modifier } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt index 94e71afeb2..df5b3e0cbb 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt @@ -48,7 +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.modifiers.onKeyboardContextMenuAction import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.textcomposer.ElementRichTextEditorStyle @@ -97,7 +97,7 @@ fun TimelineItemImageView( onClick = onContentClick, onLongClick = onLongClick, ) - .onShiftF10(onLongClick) + .onKeyboardContextMenuAction(onLongClick) } else { Modifier } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt index 79e4583ba4..1ad75fb933 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt @@ -31,7 +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.modifiers.onKeyboardContextMenuAction 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 @@ -75,7 +75,7 @@ fun TimelineItemStickerView( onLongClick = onLongClick, onLongClickLabel = stringResource(CommonStrings.action_open_context_menu), ) - .onShiftF10(onLongClick) + .onKeyboardContextMenuAction(onLongClick) } else { Modifier } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt index 433d75dbc9..87e895f76c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt @@ -54,7 +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.onKeyboardContextMenuAction import io.element.android.libraries.designsystem.modifiers.roundedBackground import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -111,7 +111,7 @@ fun TimelineItemVideoView( onClick = onContentClick, onLongClick = onLongClick, ) - .onShiftF10(onLongClick) + .onKeyboardContextMenuAction(onLongClick) } else { Modifier } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/Keyboard.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/Keyboard.kt index c1a6a57428..c8c8b8769e 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/Keyboard.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/Keyboard.kt @@ -19,12 +19,12 @@ 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. + * @param action The callback to invoke when Shift + F10 is pressed. */ -fun Modifier.onShiftF10( - onShiftF10Press: (() -> Unit)?, +fun Modifier.onKeyboardContextMenuAction( + action: (() -> Unit)?, ): Modifier = then( - if (onShiftF10Press == null) { + if (action == null) { Modifier } else { Modifier.onKeyEvent { keyEvent -> @@ -32,7 +32,7 @@ fun Modifier.onShiftF10( if (keyEvent.type == KeyEventType.KeyUp && keyEvent.isShiftPressed && keyEvent.key == Key.F10) { - onShiftF10Press() + action() true } else { false diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/AudioItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/AudioItemView.kt index f0410981b8..7229bdb9c1 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/AudioItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/AudioItemView.kt @@ -30,7 +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.modifiers.onKeyboardContextMenuAction 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 @@ -85,7 +85,7 @@ private fun FilenameRow( onLongClick = onLongClick, onLongClickLabel = stringResource(CommonStrings.action_open_context_menu), ) - .onShiftF10(onLongClick) + .onKeyboardContextMenuAction(onLongClick) .fillMaxWidth() .padding(start = 12.dp, end = 36.dp, top = 8.dp, bottom = 8.dp), verticalAlignment = Alignment.CenterVertically, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/FileItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/FileItemView.kt index bcc19212f4..2b01b734e8 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/FileItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/FileItemView.kt @@ -30,7 +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.modifiers.onKeyboardContextMenuAction 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 @@ -85,7 +85,7 @@ private fun FilenameRow( onLongClick = onLongClick, onLongClickLabel = stringResource(CommonStrings.action_open_context_menu), ) - .onShiftF10(onLongClick) + .onKeyboardContextMenuAction(onLongClick) .fillMaxWidth() .padding(start = 12.dp, end = 36.dp, top = 8.dp, bottom = 8.dp), verticalAlignment = Alignment.CenterVertically, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/ImageItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/ImageItemView.kt index f03f5e0955..05e6ccf86a 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/ImageItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/ImageItemView.kt @@ -24,7 +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.modifiers.onKeyboardContextMenuAction 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 @@ -46,7 +46,7 @@ fun ImageItemView( onLongClick = onLongClick, onLongClickLabel = stringResource(CommonStrings.action_open_context_menu), ) - .onShiftF10(onLongClick), + .onKeyboardContextMenuAction(onLongClick), ) { var isLoaded by remember { mutableStateOf(false) } AsyncImage( diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VideoItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VideoItemView.kt index 6a47567d76..4bfe58badd 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VideoItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VideoItemView.kt @@ -33,7 +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.modifiers.onKeyboardContextMenuAction 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 @@ -56,7 +56,7 @@ fun VideoItemView( onLongClick = onLongClick, onLongClickLabel = stringResource(CommonStrings.action_open_context_menu), ) - .onShiftF10(onLongClick), + .onKeyboardContextMenuAction(onLongClick), ) { var isLoaded by remember { mutableStateOf(false) } AsyncImage( diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt index 032f486cf7..23ccb9b68b 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt @@ -38,7 +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.modifiers.onKeyboardContextMenuAction 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 @@ -106,7 +106,7 @@ private fun VoiceInfoRow( onLongClick = onLongClick, onLongClickLabel = stringResource(CommonStrings.action_open_context_menu), ) - .onShiftF10(onLongClick) + .onKeyboardContextMenuAction(onLongClick) .fillMaxWidth() .padding(start = 12.dp, end = 36.dp, top = 8.dp, bottom = 8.dp), verticalAlignment = Alignment.CenterVertically, From f2580374c03cb0aefabf444eab966e1251ef138c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 5 Aug 2025 21:10:26 +0200 Subject: [PATCH 9/9] Fix comment quality --- .../kotlin/io/element/android/features/home/impl/HomeView.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt index cf1bede289..525eaf88fd 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt @@ -242,8 +242,9 @@ private fun HomeScaffold( // and include provided bottom padding // Disable contentPadding due to navigation issue using the keyboard // See https://issuetracker.google.com/issues/436432313 - bottom = 80.dp, // + padding.calculateBottomPadding(), - //top = padding.calculateTopPadding() + bottom = 80.dp, + // bottom = 80.dp + padding.calculateBottomPadding(), + // top = padding.calculateTopPadding() ), modifier = Modifier .padding(