Skip to content

Commit a4ce0a5

Browse files
committed
Add reset button to long press menu editor
1 parent 7d61741 commit a4ce0a5

4 files changed

Lines changed: 127 additions & 69 deletions

File tree

app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenu.kt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,7 @@ fun LongPressMenu(
151151
onDismissRequest = { showEditor = false },
152152
properties = DialogProperties(usePlatformDefaultWidth = false)
153153
) {
154-
ScaffoldWithToolbar(
155-
title = stringResource(R.string.long_press_menu_actions_editor),
156-
onBackClick = { showEditor = false }
157-
) { paddingValues ->
158-
LongPressMenuEditor(modifier = Modifier.padding(paddingValues))
159-
}
154+
LongPressMenuEditorPage { showEditor = false }
160155
}
161156
} else {
162157
val enabledLongPressActions by remember {

app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenuEditor.kt

Lines changed: 101 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,24 @@ import androidx.compose.material.icons.Icons
3838
import androidx.compose.material.icons.filled.ArtTrack
3939
import androidx.compose.material.icons.filled.Close
4040
import androidx.compose.material.icons.filled.DragHandle
41+
import androidx.compose.material.icons.filled.RestartAlt
42+
import androidx.compose.material3.AlertDialog
4143
import androidx.compose.material3.ExperimentalMaterial3Api
4244
import androidx.compose.material3.Icon
45+
import androidx.compose.material3.IconButton
4346
import androidx.compose.material3.LocalContentColor
4447
import androidx.compose.material3.MaterialTheme
4548
import androidx.compose.material3.Surface
4649
import androidx.compose.material3.Text
50+
import androidx.compose.material3.TextButton
4751
import androidx.compose.runtime.Composable
4852
import androidx.compose.runtime.DisposableEffect
53+
import androidx.compose.runtime.getValue
54+
import androidx.compose.runtime.mutableStateOf
4955
import androidx.compose.runtime.remember
5056
import androidx.compose.runtime.rememberCoroutineScope
57+
import androidx.compose.runtime.saveable.rememberSaveable
58+
import androidx.compose.runtime.setValue
5159
import androidx.compose.ui.Alignment
5260
import androidx.compose.ui.Modifier
5361
import androidx.compose.ui.focus.focusTarget
@@ -66,6 +74,7 @@ import androidx.compose.ui.unit.dp
6674
import androidx.compose.ui.unit.toSize
6775
import kotlin.math.floor
6876
import org.schabi.newpipe.R
77+
import org.schabi.newpipe.ui.components.common.ScaffoldWithToolbar
6978
import org.schabi.newpipe.ui.detectDragGestures
7079
import org.schabi.newpipe.ui.theme.AppTheme
7180
import org.schabi.newpipe.util.letIf
@@ -87,7 +96,7 @@ import org.schabi.newpipe.util.text.FixedHeightCenteredText
8796
* @author This composable was originally copied from FlorisBoard.
8897
*/
8998
@Composable
90-
fun LongPressMenuEditor(modifier: Modifier = Modifier) {
99+
fun LongPressMenuEditorPage(onBackClick: () -> Unit) {
91100
val context = LocalContext.current
92101
val gridState = rememberLazyGridState()
93102
val coroutineScope = rememberCoroutineScope()
@@ -101,59 +110,100 @@ fun LongPressMenuEditor(modifier: Modifier = Modifier) {
101110
}
102111
}
103112

104-
// test scrolling on Android TV by adding `.padding(horizontal = 350.dp)` here
105-
BoxWithConstraints(modifier) {
106-
// otherwise we wouldn't know the amount of columns to handle the Up/Down key events
107-
val columns = maxOf(1, floor(this.maxWidth / MinButtonWidth).toInt())
113+
ScaffoldWithToolbar(
114+
title = stringResource(R.string.long_press_menu_actions_editor),
115+
onBackClick = onBackClick,
116+
actions = {
117+
ResetToDefaultsButton(state::resetToDefaults)
118+
}
119+
) { paddingValues ->
120+
// test scrolling on Android TV by adding `.padding(horizontal = 350.dp)` here
121+
BoxWithConstraints(Modifier.padding(paddingValues)) {
122+
// otherwise we wouldn't know the amount of columns to handle the Up/Down key events
123+
val columns = maxOf(1, floor(this.maxWidth / MinButtonWidth).toInt())
108124

109-
LazyVerticalGrid(
110-
modifier = Modifier
111-
.safeDrawingPadding()
112-
.detectDragGestures(
113-
beginDragGesture = state::beginDragGesture,
114-
handleDragGestureChange = state::handleDragGestureChange,
115-
endDragGesture = state::completeDragGestureAndCleanUp
116-
)
117-
// `.focusTarget().onKeyEvent()` handles DPAD on Android TVs
118-
.focusTarget()
119-
.onKeyEvent { event -> state.onKeyEvent(event, columns) },
120-
// same width as the LongPressMenu
121-
columns = GridCells.Adaptive(MinButtonWidth),
122-
userScrollEnabled = false,
123-
state = gridState
124-
) {
125-
itemsIndexed(
126-
state.items,
127-
key = { _, item -> item.stableUniqueKey() },
128-
span = { _, item -> GridItemSpan(item.columnSpan ?: maxLineSpan) }
129-
) { i, item ->
125+
LazyVerticalGrid(
126+
modifier = Modifier
127+
.safeDrawingPadding()
128+
.detectDragGestures(
129+
beginDragGesture = state::beginDragGesture,
130+
handleDragGestureChange = state::handleDragGestureChange,
131+
endDragGesture = state::completeDragGestureAndCleanUp
132+
)
133+
// `.focusTarget().onKeyEvent()` handles DPAD on Android TVs
134+
.focusTarget()
135+
.onKeyEvent { event -> state.onKeyEvent(event, columns) },
136+
// same width as the LongPressMenu
137+
columns = GridCells.Adaptive(MinButtonWidth),
138+
userScrollEnabled = false,
139+
state = gridState
140+
) {
141+
itemsIndexed(
142+
state.items,
143+
key = { _, item -> item.stableUniqueKey() },
144+
span = { _, item -> GridItemSpan(item.columnSpan ?: maxLineSpan) }
145+
) { i, item ->
146+
ItemInListUi(
147+
item = item,
148+
selected = state.currentlyFocusedItem == i,
149+
// We only want placement animations: fade in/out animations interfere with
150+
// items being replaced by a drag marker while being dragged around, and a
151+
// fade in/out animation there does not make sense as the item was just
152+
// "picked up". Furthermore there are strange moving animation artifacts
153+
// when moving and releasing items quickly before their fade-out animation
154+
// finishes.
155+
modifier = Modifier.animateItem(fadeInSpec = null, fadeOutSpec = null)
156+
)
157+
}
158+
}
159+
state.activeDragItem?.let { activeDragItem ->
160+
// draw it the same size as the selected item,
161+
val size = with(LocalDensity.current) {
162+
remember(state.activeDragSize) { state.activeDragSize.toSize().toDpSize() }
163+
}
130164
ItemInListUi(
131-
item = item,
132-
selected = state.currentlyFocusedItem == i,
133-
// We only want placement animations: fade in/out animations interfere with
134-
// items being replaced by a drag marker while being dragged around, and a fade
135-
// in/out animation there does not make sense as the item was just "picked up".
136-
// Furthermore there are strange moving animation artifacts when moving and
137-
// releasing items quickly before their fade-out animation finishes.
138-
modifier = Modifier.animateItem(fadeInSpec = null, fadeOutSpec = null)
165+
item = activeDragItem,
166+
selected = true,
167+
modifier = Modifier
168+
.size(size)
169+
.offset { state.activeDragPosition }
170+
.offset(-size.width / 2, -size.height / 2)
171+
.offset((-24).dp, (-24).dp)
139172
)
140173
}
141174
}
142-
state.activeDragItem?.let { activeDragItem ->
143-
// draw it the same size as the selected item,
144-
val size = with(LocalDensity.current) {
145-
remember(state.activeDragSize) { state.activeDragSize.toSize().toDpSize() }
175+
}
176+
}
177+
178+
@Composable
179+
private fun ResetToDefaultsButton(onClick: () -> Unit) {
180+
var showDialog by rememberSaveable { mutableStateOf(false) }
181+
182+
if (showDialog) {
183+
AlertDialog(
184+
onDismissRequest = { showDialog = false },
185+
text = { Text(stringResource(R.string.long_press_menu_reset_to_defaults_confirm)) },
186+
confirmButton = {
187+
TextButton(onClick = {
188+
onClick()
189+
showDialog = false
190+
}) {
191+
Text(stringResource(R.string.ok))
192+
}
193+
},
194+
dismissButton = {
195+
TextButton(onClick = { showDialog = false }) {
196+
Text(stringResource(R.string.cancel))
197+
}
146198
}
147-
ItemInListUi(
148-
item = activeDragItem,
149-
selected = true,
150-
modifier = Modifier
151-
.size(size)
152-
.offset { state.activeDragPosition }
153-
.offset(-size.width / 2, -size.height / 2)
154-
.offset((-24).dp, (-24).dp)
155-
)
156-
}
199+
)
200+
}
201+
202+
IconButton(onClick = { showDialog = true }) {
203+
Icon(
204+
imageVector = Icons.Default.RestartAlt,
205+
contentDescription = stringResource(R.string.playback_reset)
206+
)
157207
}
158208
}
159209

@@ -290,11 +340,9 @@ private fun ItemInListUi(
290340
@Preview
291341
@Preview(device = "spec:width=1080px,height=1000px,dpi=440")
292342
@Composable
293-
private fun LongPressMenuEditorPreview() {
343+
private fun LongPressMenuEditorPagePreview() {
294344
AppTheme {
295-
Surface {
296-
LongPressMenuEditor()
297-
}
345+
LongPressMenuEditorPage { }
298346
}
299347
}
300348

@@ -313,7 +361,7 @@ private fun QuickActionButtonPreview(
313361
ItemInListUi(
314362
item = itemInList,
315363
selected = itemInList.stableUniqueKey() % 2 == 0,
316-
modifier = Modifier.width(MinButtonWidth)
364+
modifier = Modifier.width(MinButtonWidth * (itemInList.columnSpan ?: 4))
317365
)
318366
}
319367
}

app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenuEditorState.kt

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import androidx.compose.ui.input.key.key
2020
import androidx.compose.ui.input.key.type
2121
import androidx.compose.ui.unit.IntOffset
2222
import androidx.compose.ui.unit.IntSize
23+
import kotlin.collections.ifEmpty
2324
import kotlin.math.abs
2425
import kotlin.math.max
2526
import kotlin.math.min
@@ -49,7 +50,22 @@ class LongPressMenuEditorState(
4950
// We get the current arrangement once and do not observe on purpose.
5051
val isHeaderEnabled = loadIsHeaderEnabledFromSettings(context)
5152
val actionArrangement = loadLongPressActionArrangementFromSettings(context)
52-
sequence {
53+
return@run buildItemsInList(isHeaderEnabled, actionArrangement).toMutableStateList()
54+
}
55+
56+
// variables for handling drag, focus, and autoscrolling when finger is at top/bottom
57+
var activeDragItem by mutableStateOf<ItemInList?>(null)
58+
var activeDragPosition by mutableStateOf(IntOffset.Zero)
59+
var activeDragSize by mutableStateOf(IntSize.Zero)
60+
var currentlyFocusedItem by mutableIntStateOf(-1)
61+
var autoScrollJob by mutableStateOf<Job?>(null)
62+
var autoScrollSpeed by mutableFloatStateOf(0f)
63+
64+
private fun buildItemsInList(
65+
isHeaderEnabled: Boolean,
66+
actionArrangement: List<LongPressAction.Type>
67+
): List<ItemInList> {
68+
return sequence {
5369
yield(ItemInList.EnabledCaption)
5470
if (isHeaderEnabled) {
5571
yield(ItemInList.HeaderBox)
@@ -69,16 +85,13 @@ class LongPressMenuEditorState(
6985
.map { ItemInList.Action(it) }
7086
.ifEmpty { if (isHeaderEnabled) listOf(ItemInList.NoneMarker) else listOf() }
7187
)
72-
}.toList().toMutableStateList()
88+
}.toList()
7389
}
7490

75-
// variables for handling drag, focus, and autoscrolling when finger is at top/bottom
76-
var activeDragItem by mutableStateOf<ItemInList?>(null)
77-
var activeDragPosition by mutableStateOf(IntOffset.Zero)
78-
var activeDragSize by mutableStateOf(IntSize.Zero)
79-
var currentlyFocusedItem by mutableIntStateOf(-1)
80-
var autoScrollJob by mutableStateOf<Job?>(null)
81-
var autoScrollSpeed by mutableFloatStateOf(0f)
91+
fun resetToDefaults() {
92+
items.clear()
93+
items.addAll(buildItemsInList(true, LongPressAction.Type.DefaultEnabledActions))
94+
}
8295

8396
private fun findItemForOffsetOrClosestInRow(offset: IntOffset): LazyGridItemInfo? {
8497
var closestItemInRow: LazyGridItemInfo? = null

app/src/main/res/values/strings.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -907,7 +907,9 @@
907907
<string name="long_press_menu_enabled_actions_description">Reorder the actions by long pressing them and then dragging them around</string>
908908
<string name="long_press_menu_hidden_actions">Hidden actions:</string>
909909
<string name="long_press_menu_hidden_actions_description">Drag the header or the actions to this section to hide them</string>
910-
<string name="long_press_menu_header">Header with title, thumbnail, and clickable channel</string>
910+
<string name="long_press_menu_header">Header with thumbnail, title, clickable channel</string>
911911
<string name="back">Back</string>
912+
<string name="reset_to_defaults">Reset to defaults</string>
913+
<string name="long_press_menu_reset_to_defaults_confirm">Are you sure you want to reset to the default actions?</string>
912914
<string name="long_press_menu_actions_editor">Reorder and hide actions</string>
913915
</resources>

0 commit comments

Comments
 (0)