diff --git a/app/src/androidTest/java/org/schabi/newpipe/ui/components/menu/LongPressMenuTest.kt b/app/src/androidTest/java/org/schabi/newpipe/ui/components/menu/LongPressMenuTest.kt
index aed3a48d4..f45ed5cc0 100644
--- a/app/src/androidTest/java/org/schabi/newpipe/ui/components/menu/LongPressMenuTest.kt
+++ b/app/src/androidTest/java/org/schabi/newpipe/ui/components/menu/LongPressMenuTest.kt
@@ -127,18 +127,18 @@ class LongPressMenuTest {
private fun assertEditorIsEnteredAndExitedProperly() {
composeRule.onNodeWithContentDescription(R.string.long_press_menu_enabled_actions_description)
.assertDoesNotExist()
- composeRule.onNodeWithContentDescription(R.string.edit)
+ composeRule.onNodeWithContentDescription(R.string.long_press_menu_actions_editor)
.performClick()
composeRule.waitUntil {
composeRule.onNodeWithText(R.string.long_press_menu_enabled_actions)
.isDisplayed()
}
- composeRule.onNodeWithContentDescription(R.string.edit)
+ composeRule.onNodeWithContentDescription(R.string.long_press_menu_actions_editor)
.assertDoesNotExist()
Espresso.pressBack()
composeRule.waitUntil {
- composeRule.onNodeWithContentDescription(R.string.edit)
+ composeRule.onNodeWithContentDescription(R.string.long_press_menu_actions_editor)
.isDisplayed()
}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/Toolbar.kt b/app/src/main/java/org/schabi/newpipe/ui/Toolbar.kt
index 23a597a96..4053b70a6 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/Toolbar.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/Toolbar.kt
@@ -8,9 +8,9 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SearchBar
import androidx.compose.material3.Text
@@ -26,6 +26,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import org.schabi.newpipe.R
+import org.schabi.newpipe.ui.components.common.TooltipIconButton
import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.ui.theme.SizeTokens
@@ -70,13 +71,12 @@ fun Toolbar(
actions = {
actions()
if (hasSearch) {
- IconButton(onClick = { isSearchActive = true }) {
- Icon(
- painterResource(id = R.drawable.ic_search),
- contentDescription = stringResource(id = R.string.search),
- tint = MaterialTheme.colorScheme.onSurface
- )
- }
+ TooltipIconButton(
+ onClick = { isSearchActive = true },
+ icon = Icons.Default.Search,
+ contentDescription = stringResource(id = R.string.search),
+ tint = MaterialTheme.colorScheme.onSurface
+ )
}
}
)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/common/ScaffoldWithToolbar.kt b/app/src/main/java/org/schabi/newpipe/ui/components/common/ScaffoldWithToolbar.kt
index 050f03970..946c98254 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/common/ScaffoldWithToolbar.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/common/ScaffoldWithToolbar.kt
@@ -6,8 +6,6 @@ import androidx.compose.foundation.layout.RowScope
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
@@ -39,12 +37,11 @@ fun ScaffoldWithToolbar(
actionIconContentColor = MaterialTheme.colorScheme.onPrimaryContainer
),
navigationIcon = {
- IconButton(onClick = onBackClick) {
- Icon(
- imageVector = Icons.AutoMirrored.Filled.ArrowBack,
- contentDescription = stringResource(R.string.back)
- )
- }
+ TooltipIconButton(
+ onClick = onBackClick,
+ icon = Icons.AutoMirrored.Filled.ArrowBack,
+ contentDescription = stringResource(R.string.back)
+ )
},
actions = actions
)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/common/Tooltip.kt b/app/src/main/java/org/schabi/newpipe/ui/components/common/Tooltip.kt
new file mode 100644
index 000000000..7119d3119
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/common/Tooltip.kt
@@ -0,0 +1,84 @@
+@file:OptIn(ExperimentalMaterial3Api::class)
+
+package org.schabi.newpipe.ui.components.common
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.PlainTooltip
+import androidx.compose.material3.Text
+import androidx.compose.material3.TooltipBox
+import androidx.compose.material3.TooltipDefaults
+import androidx.compose.material3.rememberTooltipState
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
+
+/**
+ * Useful to show a descriptive popup tooltip when something (e.g. a button) is long pressed. This
+ * happens by default on XML Views buttons, but needs to be done manually in Compose.
+ *
+ * @param text the text to show in the tooltip
+ * @param modifier The [TooltipBox] implementation does not handle modifiers well, since it wraps
+ * [content] in a [Box], rendering some [content] modifiers useless. Therefore we have to wrap the
+ * [TooltipBox] in yet another [Box] with its own modifier, passed as a parameter here.
+ * @param content the content that will show a tooltip when long pressed (e.g. a button)
+ */
+@Composable
+fun SimpleTooltipBox(
+ text: String,
+ modifier: Modifier = Modifier,
+ content: @Composable () -> Unit
+) {
+ Box(modifier = modifier) {
+ TooltipBox(
+ positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
+ tooltip = { PlainTooltip { Text(text) } },
+ state = rememberTooltipState(),
+ content = content
+ )
+ }
+}
+
+/**
+ * An [IconButton] that shows a descriptive popup tooltip when it is long pressed.
+ *
+ * @param onClick handles clicks on the button
+ * @param icon the icon to show inside the button
+ * @param contentDescription the text to use as content description for the button,
+ * and also to show in the tooltip
+ * @param modifier as described in [SimpleTooltipBox]
+ * @param buttonModifier a modifier for the internal [IconButton]
+ * @param iconModifier a modifier for the internal [Icon]
+ * @param tint the color of the icon
+ */
+@Composable
+fun TooltipIconButton(
+ onClick: () -> Unit,
+ icon: ImageVector,
+ contentDescription: String,
+ modifier: Modifier = Modifier,
+ buttonModifier: Modifier = Modifier,
+ iconModifier: Modifier = Modifier,
+ tint: Color = LocalContentColor.current
+) {
+ SimpleTooltipBox(
+ text = contentDescription,
+ modifier = modifier
+ ) {
+ IconButton(
+ onClick = onClick,
+ modifier = buttonModifier
+ ) {
+ Icon(
+ icon,
+ contentDescription = contentDescription,
+ tint = tint,
+ modifier = iconModifier
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenu.kt b/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenu.kt
index 4a3c4a57e..967b6b755 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenu.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenu.kt
@@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
@@ -40,7 +41,6 @@ import androidx.compose.material3.BottomSheetDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.OutlinedButton
@@ -70,6 +70,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.Placeholder
import androidx.compose.ui.text.PlaceholderVerticalAlign
import androidx.compose.ui.text.SpanStyle
@@ -93,6 +94,8 @@ import org.schabi.newpipe.error.ErrorInfo
import org.schabi.newpipe.error.ErrorUtil
import org.schabi.newpipe.error.UserAction.LONG_PRESS_MENU_ACTION
import org.schabi.newpipe.extractor.stream.StreamType
+import org.schabi.newpipe.ui.components.common.SimpleTooltipBox
+import org.schabi.newpipe.ui.components.common.TooltipIconButton
import org.schabi.newpipe.ui.components.menu.LongPressAction.Type.EnqueueNext
import org.schabi.newpipe.ui.components.menu.LongPressAction.Type.ShowChannelDetails
import org.schabi.newpipe.ui.discardAllTouchesIf
@@ -284,7 +287,6 @@ private fun LongPressMenuContent(
enabled = action.enabled(),
modifier = Modifier
.height(buttonHeight)
- .fillMaxWidth()
.weight(1F)
)
rowIndex += 1
@@ -355,22 +357,19 @@ fun LongPressMenuDragHandle(onEditActions: () -> Unit) {
BottomSheetDefaults.DragHandle(
modifier = Modifier.align(Alignment.Center)
)
- IconButton(
+
+ // show a small button here, it's not an important button and it shouldn't
+ // capture the user attention
+ TooltipIconButton(
onClick = onEditActions,
- modifier = Modifier.align(Alignment.CenterEnd)
- ) {
- // show a small button here, it's not an important button and it shouldn't
- // capture the user attention
- Icon(
- imageVector = Icons.Default.Tune,
- contentDescription = stringResource(R.string.edit),
- // same color and height as the DragHandle
- tint = MaterialTheme.colorScheme.onSurfaceVariant,
- modifier = Modifier
- .padding(2.dp)
- .size(16.dp)
- )
- }
+ icon = Icons.Default.Tune,
+ contentDescription = stringResource(R.string.long_press_menu_actions_editor),
+ tint = MaterialTheme.colorScheme.onSurfaceVariant,
+ modifier = Modifier.align(Alignment.CenterEnd),
+ iconModifier = Modifier
+ .padding(2.dp)
+ .size(16.dp)
+ )
}
}
@@ -519,32 +518,42 @@ fun LongPressMenuHeader(
if (subtitle.isNotBlank()) {
Spacer(Modifier.height(1.dp))
- Text(
- text = subtitle,
- style = MaterialTheme.typography.bodyMedium,
- inlineContent = getSubtitleInlineContent(),
- modifier = if (onUploaderClick == null) {
- Modifier
+ if (onUploaderClick == null) {
+ LongPressMenuHeaderSubtitle(subtitle)
+ } else {
+ val label = if (item.uploader != null) {
+ stringResource(R.string.show_channel_details_for, item.uploader)
} else {
- Modifier.clickable(
- onClick = onUploaderClick,
- onClickLabel = if (item.uploader != null) {
- stringResource(R.string.show_channel_details_for, item.uploader)
- } else {
- stringResource(R.string.show_channel_details)
- }
+ stringResource(R.string.show_channel_details)
+ }
+ SimpleTooltipBox(
+ text = label
+ ) {
+ LongPressMenuHeaderSubtitle(
+ subtitle,
+ Modifier.clickable(onClick = onUploaderClick, onClickLabel = label)
)
}
- .fillMaxWidth()
- .fadedMarquee(edgeWidth = 12.dp)
- .testTag("ShowChannelDetails")
- )
+ }
}
}
}
}
}
+@Composable
+private fun LongPressMenuHeaderSubtitle(subtitle: AnnotatedString, modifier: Modifier = Modifier) {
+ Text(
+ text = subtitle,
+ style = MaterialTheme.typography.bodyMedium,
+ inlineContent = getSubtitleInlineContent(),
+ modifier = modifier
+ .fillMaxWidth()
+ .fadedMarquee(edgeWidth = 12.dp)
+ .testTag("ShowChannelDetails")
+ )
+}
+
fun getSubtitleAnnotatedString(
item: LongPressable,
showLink: Boolean,
@@ -618,30 +627,33 @@ fun LongPressMenuButton(
icon: ImageVector,
text: String,
onClick: () -> Unit,
- modifier: Modifier = Modifier,
- enabled: Boolean = true
+ enabled: Boolean,
+ modifier: Modifier = Modifier
) {
- // TODO possibly make it so that when you long-press on the button, the label appears on-screen
- // as a small popup, so in case the label text is cut off the users can still read it in full
- OutlinedButton(
- onClick = onClick,
- enabled = enabled,
- shape = MaterialTheme.shapes.large,
- contentPadding = PaddingValues(start = 3.dp, top = 8.dp, end = 3.dp, bottom = 2.dp),
- border = null,
+ SimpleTooltipBox(
+ text = text,
modifier = modifier
) {
- Column(horizontalAlignment = Alignment.CenterHorizontally) {
- Icon(
- imageVector = icon,
- contentDescription = null,
- modifier = Modifier.size(32.dp)
- )
- FixedHeightCenteredText(
- text = text,
- lines = 2,
- style = MaterialTheme.typography.bodySmall
- )
+ OutlinedButton(
+ onClick = onClick,
+ enabled = enabled,
+ shape = MaterialTheme.shapes.large,
+ contentPadding = PaddingValues(start = 3.dp, top = 8.dp, end = 3.dp, bottom = 2.dp),
+ border = null,
+ modifier = Modifier.fillMaxSize()
+ ) {
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ Icon(
+ imageVector = icon,
+ contentDescription = null,
+ modifier = Modifier.size(32.dp)
+ )
+ FixedHeightCenteredText(
+ text = text,
+ lines = 2,
+ style = MaterialTheme.typography.bodySmall
+ )
+ }
}
}
}
@@ -659,6 +671,7 @@ private fun LongPressMenuButtonPreviews() {
icon = entry.icon,
text = stringResource(entry.label),
onClick = { },
+ enabled = true,
modifier = Modifier.size(86.dp)
)
}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenuEditor.kt b/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenuEditor.kt
index c790e8d80..6d755984e 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenuEditor.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenuEditor.kt
@@ -42,7 +42,6 @@ import androidx.compose.material.icons.filled.RestartAlt
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
@@ -76,6 +75,7 @@ import kotlin.math.floor
import org.schabi.newpipe.R
import org.schabi.newpipe.ktx.letIf
import org.schabi.newpipe.ui.components.common.ScaffoldWithToolbar
+import org.schabi.newpipe.ui.components.common.TooltipIconButton
import org.schabi.newpipe.ui.detectDragGestures
import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.text.FixedHeightCenteredText
@@ -199,12 +199,11 @@ private fun ResetToDefaultsButton(onClick: () -> Unit) {
)
}
- IconButton(onClick = { showDialog = true }) {
- Icon(
- imageVector = Icons.Default.RestartAlt,
- contentDescription = stringResource(R.string.playback_reset)
- )
- }
+ TooltipIconButton(
+ onClick = { showDialog = true },
+ icon = Icons.Default.RestartAlt,
+ contentDescription = stringResource(R.string.playback_reset)
+ )
}
@Composable
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e68cd351f..5c6a394fb 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -872,7 +872,6 @@
SoundCloud has discontinued the original Top 50 charts. The corresponding tab has been removed from your main page.
Next
NewPipeExtractor is a library for extracting things from streaming sites. It is a core component of NewPipe, but could be used independently.
- Edit
- %d comment
- %d comments