Add tooltips to widgets (#1229)
* add tooltips * update toolbar tooltip * prettify ifelse statements
This commit is contained in:
parent
11ae8386fd
commit
8d7084e23f
@ -1,7 +1,6 @@
|
|||||||
package de.mm20.launcher2.ui.component
|
package de.mm20.launcher2.ui.component
|
||||||
|
|
||||||
import androidx.compose.animation.animateContentSize
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.foundation.combinedClickable
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.ColumnScope
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@ -16,16 +15,11 @@ import androidx.compose.material3.DropdownMenu
|
|||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.PlainTooltip
|
|
||||||
import androidx.compose.material3.Text
|
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.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
@ -35,7 +29,6 @@ import androidx.compose.ui.unit.dp
|
|||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.ktx.toDp
|
import de.mm20.launcher2.ui.ktx.toDp
|
||||||
import de.mm20.launcher2.ui.locals.LocalWindowPosition
|
import de.mm20.launcher2.ui.locals.LocalWindowPosition
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -57,7 +50,6 @@ fun Toolbar(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Icons(actions: List<ToolbarAction>, slots: Int) {
|
fun Icons(actions: List<ToolbarAction>, slots: Int) {
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
for (i in 0 until min(slots, actions.size)) {
|
for (i in 0 until min(slots, actions.size)) {
|
||||||
if (i == slots - 1 && slots != actions.size) {
|
if (i == slots - 1 && slots != actions.size) {
|
||||||
var showMenu by remember { mutableStateOf(false) }
|
var showMenu by remember { mutableStateOf(false) }
|
||||||
@ -82,28 +74,11 @@ fun Icons(actions: List<ToolbarAction>, slots: Int) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val action = actions[i]
|
val action = actions[i]
|
||||||
val tooltipState = rememberTooltipState()
|
Tooltip(action.label) {
|
||||||
TooltipBox(
|
|
||||||
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
|
|
||||||
tooltip = {
|
|
||||||
PlainTooltip {
|
|
||||||
Text(action.label)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
state = tooltipState
|
|
||||||
) {
|
|
||||||
when (action) {
|
when (action) {
|
||||||
is DefaultToolbarAction -> {
|
is DefaultToolbarAction -> {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = action.action,
|
onClick = action.action,
|
||||||
modifier = Modifier.combinedClickable(
|
|
||||||
onClick = {},
|
|
||||||
onLongClick = {
|
|
||||||
scope.launch {
|
|
||||||
tooltipState.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
Icon(action.icon, contentDescription = action.label)
|
Icon(action.icon, contentDescription = action.label)
|
||||||
}
|
}
|
||||||
@ -116,14 +91,6 @@ fun Icons(actions: List<ToolbarAction>, slots: Int) {
|
|||||||
onClick = {
|
onClick = {
|
||||||
showMenu = true
|
showMenu = true
|
||||||
},
|
},
|
||||||
modifier = Modifier.combinedClickable(
|
|
||||||
onClick = {},
|
|
||||||
onLongClick = {
|
|
||||||
scope.launch {
|
|
||||||
tooltipState.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
Icon(action.icon, contentDescription = action.label)
|
Icon(action.icon, contentDescription = action.label)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,28 @@
|
|||||||
|
package de.mm20.launcher2.ui.component
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Tooltip (
|
||||||
|
tooltipText: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
val tooltipState = rememberTooltipState()
|
||||||
|
|
||||||
|
TooltipBox(
|
||||||
|
state = tooltipState,
|
||||||
|
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
|
||||||
|
tooltip = { PlainTooltip { Text(tooltipText) } },
|
||||||
|
modifier = modifier
|
||||||
|
) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -49,23 +49,17 @@ import androidx.compose.material3.IconButton
|
|||||||
import androidx.compose.material3.IconButtonDefaults
|
import androidx.compose.material3.IconButtonDefaults
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.PlainTooltip
|
|
||||||
import androidx.compose.material3.Slider
|
import androidx.compose.material3.Slider
|
||||||
import androidx.compose.material3.SliderDefaults
|
import androidx.compose.material3.SliderDefaults
|
||||||
import androidx.compose.material3.Text
|
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.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.geometry.Rect
|
|
||||||
import androidx.compose.ui.graphics.StrokeCap
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
import androidx.compose.ui.graphics.asImageBitmap
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
@ -88,13 +82,13 @@ import de.mm20.launcher2.music.PlaybackState
|
|||||||
import de.mm20.launcher2.music.SupportedActions
|
import de.mm20.launcher2.music.SupportedActions
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||||
|
import de.mm20.launcher2.ui.component.Tooltip
|
||||||
import de.mm20.launcher2.ui.ktx.conditional
|
import de.mm20.launcher2.ui.ktx.conditional
|
||||||
import de.mm20.launcher2.ui.launcher.transitions.EnterHomeTransitionParams
|
import de.mm20.launcher2.ui.launcher.transitions.EnterHomeTransitionParams
|
||||||
import de.mm20.launcher2.ui.launcher.transitions.HandleEnterHomeTransition
|
import de.mm20.launcher2.ui.launcher.transitions.HandleEnterHomeTransition
|
||||||
import de.mm20.launcher2.ui.locals.LocalCardStyle
|
import de.mm20.launcher2.ui.locals.LocalCardStyle
|
||||||
import de.mm20.launcher2.ui.locals.LocalWindowSize
|
import de.mm20.launcher2.ui.locals.LocalWindowSize
|
||||||
import de.mm20.launcher2.widgets.MusicWidget
|
import de.mm20.launcher2.widgets.MusicWidget
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -334,44 +328,58 @@ fun MusicWidget(widget: MusicWidget) {
|
|||||||
|
|
||||||
) {
|
) {
|
||||||
if (supportedActions.skipToPrevious) {
|
if (supportedActions.skipToPrevious) {
|
||||||
IconButton(
|
Tooltip(
|
||||||
onClick = {
|
tooltipText = stringResource(R.string.music_widget_previous_track)
|
||||||
viewModel.skipPrevious()
|
) {
|
||||||
}) {
|
IconButton(
|
||||||
Icon(
|
onClick = {
|
||||||
imageVector = Icons.Rounded.SkipPrevious,
|
viewModel.skipPrevious()
|
||||||
stringResource(R.string.music_widget_previous_track)
|
}) {
|
||||||
)
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.SkipPrevious,
|
||||||
|
stringResource(R.string.music_widget_previous_track)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val playPauseIcon =
|
val playPauseIcon =
|
||||||
AnimatedImageVector.animatedVectorResource(R.drawable.anim_ic_play_pause)
|
AnimatedImageVector.animatedVectorResource(R.drawable.anim_ic_play_pause)
|
||||||
FilledTonalIconButton(
|
Tooltip(
|
||||||
colors = IconButtonDefaults.filledTonalIconButtonColors(
|
tooltipText = stringResource(
|
||||||
containerColor = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = LocalCardStyle.current.opacity),
|
if (playbackState == PlaybackState.Playing) R.string.music_widget_pause
|
||||||
),
|
else R.string.music_widget_play
|
||||||
onClick = { viewModel.togglePause() },
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
painter = rememberAnimatedVectorPainter(
|
|
||||||
playPauseIcon,
|
|
||||||
atEnd = playbackState == PlaybackState.Playing
|
|
||||||
),
|
|
||||||
contentDescription = if (playbackState == PlaybackState.Playing) {
|
|
||||||
stringResource(R.string.music_widget_pause)
|
|
||||||
} else {
|
|
||||||
stringResource(R.string.music_widget_play)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
) {
|
||||||
|
FilledTonalIconButton(
|
||||||
|
colors = IconButtonDefaults.filledTonalIconButtonColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = LocalCardStyle.current.opacity),
|
||||||
|
),
|
||||||
|
onClick = { viewModel.togglePause() },
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = rememberAnimatedVectorPainter(
|
||||||
|
playPauseIcon,
|
||||||
|
atEnd = playbackState == PlaybackState.Playing
|
||||||
|
),
|
||||||
|
contentDescription = stringResource(
|
||||||
|
if (playbackState == PlaybackState.Playing) R.string.music_widget_pause
|
||||||
|
else R.string.music_widget_play
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (supportedActions.skipToNext) {
|
if (supportedActions.skipToNext) {
|
||||||
IconButton(onClick = {
|
Tooltip(
|
||||||
viewModel.skipNext()
|
tooltipText = stringResource(R.string.music_widget_next_track)
|
||||||
}) {
|
) {
|
||||||
Icon(
|
IconButton(onClick = {
|
||||||
imageVector = Icons.Rounded.SkipNext,
|
viewModel.skipNext()
|
||||||
stringResource(R.string.music_widget_next_track)
|
}) {
|
||||||
)
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.SkipNext,
|
||||||
|
stringResource(R.string.music_widget_next_track)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CustomActions(
|
CustomActions(
|
||||||
@ -394,29 +402,12 @@ fun CustomActions(
|
|||||||
val usedSlots = 1 + (if (actions.skipToPrevious) 1 else 0) + (if (actions.skipToNext) 1 else 0)
|
val usedSlots = 1 + (if (actions.skipToPrevious) 1 else 0) + (if (actions.skipToNext) 1 else 0)
|
||||||
val slots = 5 - usedSlots
|
val slots = 5 - usedSlots
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
for (i in 0 until min(actions.customActions.size, slots - 1)) {
|
for (i in 0 until min(actions.customActions.size, slots - 1)) {
|
||||||
val action = actions.customActions[i]
|
val action = actions.customActions[i]
|
||||||
val tooltipState = rememberTooltipState()
|
Tooltip(
|
||||||
TooltipBox(
|
tooltipText = action.name.toString()
|
||||||
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
|
|
||||||
state = tooltipState,
|
|
||||||
tooltip = {
|
|
||||||
PlainTooltip {
|
|
||||||
Text(action.name.toString())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
) {
|
) {
|
||||||
IconButton(
|
IconButton(
|
||||||
modifier = Modifier.combinedClickable(
|
|
||||||
onClick = {},
|
|
||||||
onLongClick = {
|
|
||||||
scope.launch {
|
|
||||||
tooltipState.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
onClick = {
|
onClick = {
|
||||||
onActionSelected(action)
|
onActionSelected(action)
|
||||||
}
|
}
|
||||||
@ -428,8 +419,12 @@ fun CustomActions(
|
|||||||
if (slots < actions.customActions.size) {
|
if (slots < actions.customActions.size) {
|
||||||
var showOverflowMenu by remember { mutableStateOf(false) }
|
var showOverflowMenu by remember { mutableStateOf(false) }
|
||||||
Box {
|
Box {
|
||||||
IconButton(onClick = { showOverflowMenu = true }) {
|
Tooltip(
|
||||||
Icon(imageVector = Icons.Rounded.MoreVert, contentDescription = null)
|
tooltipText = stringResource(R.string.action_more_actions)
|
||||||
|
) {
|
||||||
|
IconButton(onClick = { showOverflowMenu = true }) {
|
||||||
|
Icon(imageVector = Icons.Rounded.MoreVert, contentDescription = null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DropdownMenu(
|
DropdownMenu(
|
||||||
expanded = showOverflowMenu,
|
expanded = showOverflowMenu,
|
||||||
@ -456,26 +451,11 @@ fun CustomActions(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (slots == actions.customActions.size) {
|
} else if (slots == actions.customActions.size) {
|
||||||
val tooltipState = rememberTooltipState()
|
|
||||||
val action = actions.customActions.last()
|
val action = actions.customActions.last()
|
||||||
TooltipBox(
|
Tooltip (
|
||||||
state = tooltipState,
|
tooltipText = action.name.toString()
|
||||||
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
|
|
||||||
tooltip = {
|
|
||||||
PlainTooltip {
|
|
||||||
Text(action.name.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
IconButton(
|
IconButton(
|
||||||
modifier = Modifier.combinedClickable(
|
|
||||||
onClick = {},
|
|
||||||
onLongClick = {
|
|
||||||
scope.launch {
|
|
||||||
tooltipState.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
onClick = {
|
onClick = {
|
||||||
onActionSelected(action)
|
onActionSelected(action)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,22 +43,17 @@ import androidx.compose.material3.DropdownMenuItem
|
|||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.PlainTooltip
|
|
||||||
import androidx.compose.material3.SnackbarDuration
|
import androidx.compose.material3.SnackbarDuration
|
||||||
import androidx.compose.material3.SnackbarResult
|
import androidx.compose.material3.SnackbarResult
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TooltipBox
|
|
||||||
import androidx.compose.material3.TooltipDefaults
|
|
||||||
import androidx.compose.material3.rememberTooltipState
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@ -74,6 +69,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
|||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.Banner
|
import de.mm20.launcher2.ui.component.Banner
|
||||||
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
||||||
|
import de.mm20.launcher2.ui.component.Tooltip
|
||||||
import de.mm20.launcher2.ui.component.markdown.MarkdownEditor
|
import de.mm20.launcher2.ui.component.markdown.MarkdownEditor
|
||||||
import de.mm20.launcher2.ui.component.markdown.MarkdownText
|
import de.mm20.launcher2.ui.component.markdown.MarkdownText
|
||||||
import de.mm20.launcher2.ui.locals.LocalSnackbarHostState
|
import de.mm20.launcher2.ui.locals.LocalSnackbarHostState
|
||||||
@ -94,8 +90,6 @@ fun NotesWidget(
|
|||||||
val snackbarHostState = LocalSnackbarHostState.current
|
val snackbarHostState = LocalSnackbarHostState.current
|
||||||
val lifecycleOwner = LocalLifecycleOwner.current
|
val lifecycleOwner = LocalLifecycleOwner.current
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
var showConflictResolveSheet by remember { mutableStateOf(false) }
|
var showConflictResolveSheet by remember { mutableStateOf(false) }
|
||||||
var readWriteErrorSheetText by remember { mutableStateOf<String?>(null) }
|
var readWriteErrorSheetText by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
@ -181,20 +175,12 @@ fun NotesWidget(
|
|||||||
Row(
|
Row(
|
||||||
modifier = Modifier.padding(8.dp),
|
modifier = Modifier.padding(8.dp),
|
||||||
) {
|
) {
|
||||||
val tooltipState = rememberTooltipState()
|
Tooltip(
|
||||||
TooltipBox(
|
tooltipText = stringResource(
|
||||||
state = tooltipState,
|
if (widget.config.linkedFile == null) R.string.note_widget_link_file
|
||||||
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
|
else R.string.note_widget_action_unlink_file
|
||||||
tooltip = {
|
),
|
||||||
PlainTooltip {
|
) {
|
||||||
Text(
|
|
||||||
stringResource(
|
|
||||||
if (widget.config.linkedFile == null) R.string.note_widget_link_file
|
|
||||||
else R.string.note_widget_action_unlink_file
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
if (widget.config.linkedFile == null) {
|
if (widget.config.linkedFile == null) {
|
||||||
@ -205,14 +191,6 @@ fun NotesWidget(
|
|||||||
viewModel.unlinkFile(context)
|
viewModel.unlinkFile(context)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.combinedClickable(
|
|
||||||
onClick = {},
|
|
||||||
onLongClick = {
|
|
||||||
scope.launch {
|
|
||||||
tooltipState.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
if (widget.config.linkedFile == null) Icons.Rounded.Link
|
if (widget.config.linkedFile == null) Icons.Rounded.Link
|
||||||
@ -225,26 +203,13 @@ fun NotesWidget(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isLastWidget == false) {
|
if (isLastWidget == false) {
|
||||||
TooltipBox(
|
Tooltip(
|
||||||
state = tooltipState,
|
tooltipText = stringResource(R.string.notes_widget_action_dismiss)
|
||||||
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
|
) {
|
||||||
tooltip = {
|
|
||||||
PlainTooltip {
|
|
||||||
Text(stringResource(R.string.notes_widget_action_dismiss))
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.dismissNote()
|
viewModel.dismissNote()
|
||||||
},
|
},
|
||||||
modifier = Modifier.combinedClickable(
|
|
||||||
onClick = {},
|
|
||||||
onLongClick = {
|
|
||||||
scope.launch {
|
|
||||||
tooltipState.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
Icon(Icons.Rounded.Delete, null)
|
Icon(Icons.Rounded.Delete, null)
|
||||||
}
|
}
|
||||||
@ -297,8 +262,12 @@ fun NotesWidget(
|
|||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
Box {
|
Box {
|
||||||
IconButton(onClick = { showMenu = true }) {
|
Tooltip(
|
||||||
Icon(Icons.Rounded.MoreVert, stringResource(R.string.action_more_actions))
|
tooltipText = stringResource(R.string.action_more_actions)
|
||||||
|
) {
|
||||||
|
IconButton(onClick = { showMenu = true }) {
|
||||||
|
Icon(Icons.Rounded.MoreVert, stringResource(R.string.action_more_actions))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DropdownMenu(expanded = showMenu, onDismissRequest = { showMenu = false }) {
|
DropdownMenu(expanded = showMenu, onDismissRequest = { showMenu = false }) {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
|
|||||||
@ -67,6 +67,7 @@ import de.mm20.launcher2.ui.R
|
|||||||
import de.mm20.launcher2.ui.common.WeatherLocationSearchDialog
|
import de.mm20.launcher2.ui.common.WeatherLocationSearchDialog
|
||||||
import de.mm20.launcher2.ui.component.Banner
|
import de.mm20.launcher2.ui.component.Banner
|
||||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||||
|
import de.mm20.launcher2.ui.component.Tooltip
|
||||||
import de.mm20.launcher2.ui.component.weather.AnimatedWeatherIcon
|
import de.mm20.launcher2.ui.component.weather.AnimatedWeatherIcon
|
||||||
import de.mm20.launcher2.ui.component.weather.WeatherIcon
|
import de.mm20.launcher2.ui.component.weather.WeatherIcon
|
||||||
import de.mm20.launcher2.ui.ktx.blendIntoViewScale
|
import de.mm20.launcher2.ui.ktx.blendIntoViewScale
|
||||||
@ -231,32 +232,36 @@ fun CurrentWeather(forecast: Forecast, imperialUnits: Boolean) {
|
|||||||
style = MaterialTheme.typography.titleMedium
|
style = MaterialTheme.typography.titleMedium
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Surface(
|
Tooltip(
|
||||||
shape = MaterialTheme.shapes.extraSmall.copy(
|
tooltipText = stringResource(R.string.preference_weather_provider)
|
||||||
topStart = CornerSize(0),
|
|
||||||
topEnd = CornerSize(0),
|
|
||||||
bottomEnd = CornerSize(0)
|
|
||||||
),
|
|
||||||
color = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = LocalCardStyle.current.opacity),
|
|
||||||
) {
|
) {
|
||||||
Text(
|
Surface(
|
||||||
text = "${forecast.provider} (${
|
shape = MaterialTheme.shapes.extraSmall.copy(
|
||||||
formatTime(
|
topStart = CornerSize(0),
|
||||||
LocalContext.current,
|
topEnd = CornerSize(0),
|
||||||
forecast.updateTime
|
bottomEnd = CornerSize(0)
|
||||||
)
|
),
|
||||||
})",
|
color = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = LocalCardStyle.current.opacity),
|
||||||
style = MaterialTheme.typography.bodySmall.copy(fontSize = 8.sp),
|
) {
|
||||||
modifier = Modifier
|
Text(
|
||||||
.clickable(onClick = {
|
text = "${forecast.provider} (${
|
||||||
val intent = Intent(Intent.ACTION_VIEW).apply {
|
formatTime(
|
||||||
data = Uri.parse(forecast.providerUrl)
|
LocalContext.current,
|
||||||
?: return@clickable
|
forecast.updateTime
|
||||||
}
|
)
|
||||||
context.tryStartActivity(intent)
|
})",
|
||||||
})
|
style = MaterialTheme.typography.bodySmall.copy(fontSize = 8.sp),
|
||||||
.padding(start = 8.dp, top = 4.dp, bottom = 4.dp, end = 12.dp)
|
modifier = Modifier
|
||||||
)
|
.clickable(onClick = {
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||||
|
data = Uri.parse(forecast.providerUrl)
|
||||||
|
?: return@clickable
|
||||||
|
}
|
||||||
|
context.tryStartActivity(intent)
|
||||||
|
})
|
||||||
|
.padding(start = 8.dp, top = 4.dp, bottom = 4.dp, end = 12.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Row(
|
Row(
|
||||||
@ -293,71 +298,84 @@ fun CurrentWeather(forecast: Forecast, imperialUnits: Boolean) {
|
|||||||
horizontalArrangement = Arrangement.SpaceBetween
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
) {
|
) {
|
||||||
if (forecast.humidity != null) {
|
if (forecast.humidity != null) {
|
||||||
Row(
|
Tooltip(
|
||||||
verticalAlignment = Alignment.CenterVertically
|
tooltipText = stringResource(R.string.weather_forecast_humidity)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Row(
|
||||||
imageVector = Icons.Rounded.HumidityPercentage,
|
verticalAlignment = Alignment.CenterVertically
|
||||||
modifier = Modifier.size(20.dp),
|
) {
|
||||||
tint = MaterialTheme.colorScheme.secondary,
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.padding(3.dp))
|
|
||||||
Text(
|
|
||||||
text = "${forecast.humidity!!.roundToInt()} %",
|
|
||||||
style = MaterialTheme.typography.bodySmall,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (forecast.windDirection != null || forecast.windSpeed != null) {
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
if (forecast.windDirection != null) {
|
|
||||||
// windDirection is "fromDirection"; Wind (arrow) blows into opposite direction
|
|
||||||
val angle by animateFloatAsState(forecast.windDirection!!.toFloat() + 180f)
|
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Rounded.North,
|
imageVector = Icons.Rounded.HumidityPercentage,
|
||||||
modifier = Modifier
|
|
||||||
.rotate(angle)
|
|
||||||
.size(20.dp),
|
|
||||||
contentDescription = null,
|
|
||||||
tint = MaterialTheme.colorScheme.secondary,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Rounded.Air,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(20.dp),
|
modifier = Modifier.size(20.dp),
|
||||||
tint = MaterialTheme.colorScheme.secondary,
|
tint = MaterialTheme.colorScheme.secondary,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.padding(3.dp))
|
||||||
|
Text(
|
||||||
|
text = "${forecast.humidity!!.roundToInt()} %",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.padding(3.dp))
|
}
|
||||||
Text(
|
|
||||||
text = if (forecast.windSpeed != null) {
|
}
|
||||||
formatWindSpeed(imperialUnits, forecast)
|
if (forecast.windDirection != null || forecast.windSpeed != null) {
|
||||||
|
Tooltip(
|
||||||
|
tooltipText = stringResource(R.string.weather_forecast_wind)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
if (forecast.windDirection != null) {
|
||||||
|
// windDirection is "fromDirection"; Wind (arrow) blows into opposite direction
|
||||||
|
val angle by animateFloatAsState(forecast.windDirection!!.toFloat() + 180f)
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.North,
|
||||||
|
modifier = Modifier
|
||||||
|
.rotate(angle)
|
||||||
|
.size(20.dp),
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.secondary,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
windDirectionAsWord(forecast.windDirection!!)
|
Icon(
|
||||||
},
|
imageVector = Icons.Rounded.Air,
|
||||||
style = MaterialTheme.typography.bodySmall,
|
contentDescription = null,
|
||||||
)
|
modifier = Modifier.size(20.dp),
|
||||||
|
tint = MaterialTheme.colorScheme.secondary,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.padding(3.dp))
|
||||||
|
Text(
|
||||||
|
text = if (forecast.windSpeed != null) {
|
||||||
|
formatWindSpeed(imperialUnits, forecast)
|
||||||
|
} else {
|
||||||
|
windDirectionAsWord(forecast.windDirection!!)
|
||||||
|
},
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (forecast.precipitation != null) {
|
if (forecast.precipitation != null) {
|
||||||
Row(
|
Tooltip(
|
||||||
verticalAlignment = Alignment.CenterVertically
|
tooltipText = stringResource(id = R.string.weather_forecast_precipitation)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Row(
|
||||||
imageVector = Icons.Rounded.Rain,
|
verticalAlignment = Alignment.CenterVertically
|
||||||
modifier = Modifier.size(20.dp),
|
) {
|
||||||
contentDescription = null,
|
Icon(
|
||||||
tint = MaterialTheme.colorScheme.secondary,
|
imageVector = Icons.Rounded.Rain,
|
||||||
)
|
modifier = Modifier.size(20.dp),
|
||||||
Spacer(modifier = Modifier.padding(3.dp))
|
contentDescription = null,
|
||||||
Text(
|
tint = MaterialTheme.colorScheme.secondary,
|
||||||
text = formatPrecipitation(imperialUnits, forecast),
|
)
|
||||||
style = MaterialTheme.typography.bodySmall,
|
Spacer(modifier = Modifier.padding(3.dp))
|
||||||
)
|
Text(
|
||||||
|
text = formatPrecipitation(imperialUnits, forecast),
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,17 +16,12 @@ import androidx.compose.material.icons.rounded.SettingsSuggest
|
|||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.PlainTooltip
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TooltipBox
|
|
||||||
import androidx.compose.material3.TooltipDefaults
|
|
||||||
import androidx.compose.material3.rememberTooltipState
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@ -36,6 +31,7 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
||||||
|
import de.mm20.launcher2.ui.component.Tooltip
|
||||||
import de.mm20.launcher2.ui.component.colorpicker.HctColorPicker
|
import de.mm20.launcher2.ui.component.colorpicker.HctColorPicker
|
||||||
import de.mm20.launcher2.ui.component.colorpicker.rememberHctColorPickerState
|
import de.mm20.launcher2.ui.component.colorpicker.rememberHctColorPickerState
|
||||||
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||||
@ -51,17 +47,8 @@ fun CorePaletteColorPreference(
|
|||||||
) {
|
) {
|
||||||
var showDialog by remember { mutableStateOf(false) }
|
var showDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
Tooltip(
|
||||||
val tooltipState = rememberTooltipState()
|
tooltipText = title
|
||||||
|
|
||||||
TooltipBox(
|
|
||||||
state = tooltipState,
|
|
||||||
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
|
|
||||||
tooltip = {
|
|
||||||
PlainTooltip {
|
|
||||||
Text(title)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
) {
|
) {
|
||||||
ColorSwatch(
|
ColorSwatch(
|
||||||
color = Color(value ?: defaultValue),
|
color = Color(value ?: defaultValue),
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import androidx.compose.animation.AnimatedContent
|
|||||||
import androidx.compose.foundation.Canvas
|
import androidx.compose.foundation.Canvas
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.combinedClickable
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@ -25,21 +24,16 @@ import androidx.compose.material3.ButtonDefaults
|
|||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.PlainTooltip
|
|
||||||
import androidx.compose.material3.SegmentedButton
|
import androidx.compose.material3.SegmentedButton
|
||||||
import androidx.compose.material3.SegmentedButtonDefaults
|
import androidx.compose.material3.SegmentedButtonDefaults
|
||||||
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
||||||
import androidx.compose.material3.Slider
|
import androidx.compose.material3.Slider
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TooltipBox
|
|
||||||
import androidx.compose.material3.TooltipDefaults
|
|
||||||
import androidx.compose.material3.rememberTooltipState
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@ -61,11 +55,11 @@ import de.mm20.launcher2.themes.atTone
|
|||||||
import de.mm20.launcher2.themes.get
|
import de.mm20.launcher2.themes.get
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
||||||
|
import de.mm20.launcher2.ui.component.Tooltip
|
||||||
import de.mm20.launcher2.ui.component.colorpicker.HctColorPicker
|
import de.mm20.launcher2.ui.component.colorpicker.HctColorPicker
|
||||||
import de.mm20.launcher2.ui.component.colorpicker.rememberHctColorPickerState
|
import de.mm20.launcher2.ui.component.colorpicker.rememberHctColorPickerState
|
||||||
import de.mm20.launcher2.ui.ktx.hct
|
import de.mm20.launcher2.ui.ktx.hct
|
||||||
import hct.Hct
|
import hct.Hct
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import de.mm20.launcher2.themes.Color as ThemeColor
|
import de.mm20.launcher2.themes.Color as ThemeColor
|
||||||
|
|
||||||
@ -80,25 +74,15 @@ fun ThemeColorPreference(
|
|||||||
) {
|
) {
|
||||||
var showDialog by remember { mutableStateOf(false) }
|
var showDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
Tooltip(
|
||||||
val tooltipState = rememberTooltipState()
|
tooltipText = title
|
||||||
|
|
||||||
TooltipBox(
|
|
||||||
state = tooltipState,
|
|
||||||
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
|
|
||||||
tooltip = { PlainTooltip { Text(title) } }
|
|
||||||
) {
|
) {
|
||||||
ColorSwatch(
|
ColorSwatch(
|
||||||
color = Color((value ?: defaultValue).get(corePalette)),
|
color = Color((value ?: defaultValue).get(corePalette)),
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.size(48.dp)
|
.size(48.dp)
|
||||||
.combinedClickable(
|
.clickable(
|
||||||
onClick = { showDialog = true },
|
onClick = { showDialog = true },
|
||||||
onLongClick = {
|
|
||||||
scope.launch {
|
|
||||||
tooltipState.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1005,4 +1005,7 @@
|
|||||||
<item quantity="other">%1$s lists selected</item>
|
<item quantity="other">%1$s lists selected</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="reset_icon">Reset icon</string>
|
<string name="reset_icon">Reset icon</string>
|
||||||
|
<string name="weather_forecast_humidity">Humidity</string>
|
||||||
|
<string name="weather_forecast_wind">Wind</string>
|
||||||
|
<string name="weather_forecast_precipitation">Precipitation</string>
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
x
Reference in New Issue
Block a user