Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions manager-ui/src/main/kotlin/org/matrix/vector/ui/ContextClick.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.matrix.vector.ui

import androidx.compose.foundation.combinedClickable
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.PointerEventPass
import androidx.compose.ui.input.pointer.isSecondaryPressed
import androidx.compose.ui.input.pointer.pointerInput

/**
* A row's whole gesture set: tap it, hold it — and, from a mouse or a touchpad, right-click it.
*
* A tablet with a keyboard case is a pointer device, and the way to ask a row for its menu with a
* pointer is the secondary button: a right click, or a two-finger tap on the touchpad. Compose has
* no notion of that gesture. `combinedClickable` reads a press from any button as the beginning of
* an ordinary tap and reaches [onLongClick] only by timing a held finger, so on a touchpad every
* row here answered a right click by doing whatever a left click does, and whatever the hold was
* for could not be reached at all. The View-based manager never had to say any of this: a `View`
* opens its registered context menu on a secondary press by itself, which is why the gesture worked
* before the manager was written in Compose.
*
* So the secondary press is caught here and handed to [onLongClick] — the same action, because the
* two gestures mean the same thing, one asked with a finger and one asked with a pointer.
*/
fun Modifier.contextClickable(onClick: () -> Unit, onLongClick: (() -> Unit)? = null): Modifier =
combinedClickable(onClick = onClick, onLongClick = onLongClick)
.then(if (onLongClick == null) Modifier else Modifier.onSecondaryPress(onLongClick))

/**
* The secondary button's press, taken before anything underneath can read it as a tap.
*
* On [Initial][PointerEventPass.Initial], the pass that runs before the one `combinedClickable`
* listens on — the same way [org.matrix.vector.ui.navigation.PanelBar] gets ahead of the item it
* wraps — and every event of the press is consumed, which is what keeps the click from landing:
* foundation asks for a press nothing else has taken, and for a release nothing else has taken.
*
* The action runs on the way down rather than on the release, matching the platform: a right click
* on a `View` opens its context menu the moment the button goes down. It runs on the first event
* that reports the button held rather than on the press event alone, because a pointer that
* announces its buttons a moment after it announces the touch would otherwise be missed.
*/
private fun Modifier.onSecondaryPress(action: () -> Unit): Modifier =
pointerInput(action) {
awaitPointerEventScope {
var fired = false
while (true) {
val event = awaitPointerEvent(PointerEventPass.Initial)
if (!event.buttons.isSecondaryPressed) {
fired = false
continue
}
event.changes.forEach { it.consume() }
if (!fired) {
fired = true
action()
}
}
}
}
5 changes: 2 additions & 3 deletions manager-ui/src/main/kotlin/org/matrix/vector/ui/ModuleRow.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
Expand Down Expand Up @@ -135,7 +134,7 @@ fun ModuleRow(
Column(
modifier =
if (onIconClick != null)
Modifier.combinedClickable(onClick = onIconClick, onLongClick = onIconLongClick)
Modifier.contextClickable(onClick = onIconClick, onLongClick = onIconLongClick)
else Modifier,
// Against the text, not centred over the badge: the badge below is wider than the icon,
// so centring left a gap between the icon and the edge the names all start from.
Expand Down Expand Up @@ -175,7 +174,7 @@ fun ModuleRow(
Modifier.weight(1f)
.then(
if (onClick != null)
Modifier.combinedClickable(onClick = onClick, onLongClick = onLongClick)
Modifier.contextClickable(onClick = onClick, onLongClick = onLongClick)
else Modifier
)
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import org.matrix.vector.ui.contextClickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
Expand Down Expand Up @@ -212,7 +212,7 @@ fun CommitRow(
// means "open this commit", and a long press on a subject line has no obvious
// subject; a long press on a name plainly means *that name*.
modifier =
Modifier.combinedClickable(
Modifier.contextClickable(
onClick = { onOpenCommit(commit) },
onLongClick = {
haptics.performHapticFeedback(HapticFeedbackType.LongPress)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable
import org.matrix.vector.ui.contextClickable
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.size
Expand Down Expand Up @@ -922,7 +922,7 @@ private fun ContributorRow(
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier =
Modifier.combinedClickable(
Modifier.contextClickable(
onClick = { if (hasProfile) onClick(person) },
onLongClick = {
haptics.performHapticFeedback(HapticFeedbackType.LongPress)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.border
import androidx.compose.foundation.combinedClickable
import org.matrix.vector.ui.contextClickable
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
Expand Down Expand Up @@ -960,7 +960,7 @@ private fun AppRow(

ListItem(
modifier =
Modifier.combinedClickable(
Modifier.contextClickable(
onClick = { if (enabled) onToggle(!app.isSelectedInScope) },
onLongClick = {
// The long press is where re-optimize lives, and re-optimize is the fix
Expand Down
Loading