From da5b14f936568a5fd9a3833961defe153d7a769d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Gerlei?= Date: Wed, 7 May 2025 08:23:02 +0200 Subject: [PATCH 1/5] Wrap Icon in TopMenuButtonImpl in a TooltipBox to be able to show a tooltip for Toolbar menu items - On Android, long pressing on a Toolbar menu item should display the menu items' names in a tooltip - The Tooltip's background color is not correct yet in light mode (it uses a dark color for some reason) - Content description is used as the tooltip text --- .../kotlinconf/ui/components/TopMenuButton.kt | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/TopMenuButton.kt b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/TopMenuButton.kt index 850679aa..a5919f29 100644 --- a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/TopMenuButton.kt +++ b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/TopMenuButton.kt @@ -8,6 +8,12 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.selection.toggleable import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.PlainTooltip +import androidx.compose.material3.TooltipBox +import androidx.compose.material3.TooltipDefaults +import androidx.compose.material3.rememberTooltipState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -28,6 +34,7 @@ import androidx.compose.ui.tooling.preview.Preview import org.jetbrains.kotlinconf.ui.theme.KotlinConfTheme import org.jetbrains.kotlinconf.ui.theme.PreviewHelper +@OptIn(ExperimentalMaterial3Api::class) @Composable private fun TopMenuButtonImpl( icon: DrawableResource, @@ -37,18 +44,24 @@ private fun TopMenuButtonImpl( iconColor: Color, modifier: Modifier = Modifier, ) { - Icon( - modifier = modifier - .padding(6.dp) - .size(36.dp) - .clip(CircleShape) - .then(interactionModifier) - .background(backgroundColor) - .padding(6.dp), - painter = painterResource(icon), - contentDescription = contentDescription, - tint = iconColor, - ) + TooltipBox( + positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), + tooltip = { PlainTooltip { Text(contentDescription) } }, + state = rememberTooltipState() + ) { + Icon( + modifier = modifier + .padding(6.dp) + .size(36.dp) + .clip(CircleShape) + .then(interactionModifier) + .background(backgroundColor) + .padding(6.dp), + painter = painterResource(icon), + contentDescription = contentDescription, + tint = iconColor, + ) + } } /** From 921d2b247b22b5637fe9274e8a5559f5708d4c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Gerlei?= Date: Mon, 10 Nov 2025 07:30:42 +0100 Subject: [PATCH 2/5] Use new API for TooltipBoxPositionProvider instead of Deprecated one --- .../org/jetbrains/kotlinconf/ui/components/TopMenuButton.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/TopMenuButton.kt b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/TopMenuButton.kt index a5919f29..b1f29941 100644 --- a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/TopMenuButton.kt +++ b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/TopMenuButton.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.PlainTooltip +import androidx.compose.material3.TooltipAnchorPosition import androidx.compose.material3.TooltipBox import androidx.compose.material3.TooltipDefaults import androidx.compose.material3.rememberTooltipState @@ -23,6 +24,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.semantics.Role +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import kotlinconfapp.ui_components.generated.resources.UiRes import kotlinconfapp.ui_components.generated.resources.bookmark_24 @@ -30,7 +32,6 @@ import kotlinconfapp.ui_components.generated.resources.close_24 import kotlinconfapp.ui_components.generated.resources.search_24 import org.jetbrains.compose.resources.DrawableResource import org.jetbrains.compose.resources.painterResource -import androidx.compose.ui.tooling.preview.Preview import org.jetbrains.kotlinconf.ui.theme.KotlinConfTheme import org.jetbrains.kotlinconf.ui.theme.PreviewHelper @@ -45,7 +46,7 @@ private fun TopMenuButtonImpl( modifier: Modifier = Modifier, ) { TooltipBox( - positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), + positionProvider = TooltipDefaults.rememberTooltipPositionProvider(TooltipAnchorPosition.Below), tooltip = { PlainTooltip { Text(contentDescription) } }, state = rememberTooltipState() ) { From 60b78535ffa007a580c5e0aadfa7e0ccdea79ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Gerlei?= Date: Mon, 10 Nov 2025 07:34:40 +0100 Subject: [PATCH 3/5] Set background and text colors for TooltipBox explicitly - Set containerColor (background) to a dark color and text color to a light in light mode and the inverse on dark (it is done this way in another apps) - Make primaryTextInverted really inverted in dark mode too - Add two new colors: blueGrey and offWhite, based on what other apps use as tooltip background colors - Add new tooltipBackground color to app theme Colors, use primaryTextInverted for Tooltip text color --- .../kotlinconf/ui/components/TopMenuButton.kt | 11 ++++++++++- .../org/jetbrains/kotlinconf/ui/theme/ColorValues.kt | 4 ++++ .../org/jetbrains/kotlinconf/ui/theme/Colors.kt | 10 +++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/TopMenuButton.kt b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/TopMenuButton.kt index b1f29941..8210ff94 100644 --- a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/TopMenuButton.kt +++ b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/TopMenuButton.kt @@ -47,7 +47,16 @@ private fun TopMenuButtonImpl( ) { TooltipBox( positionProvider = TooltipDefaults.rememberTooltipPositionProvider(TooltipAnchorPosition.Below), - tooltip = { PlainTooltip { Text(contentDescription) } }, + tooltip = { + PlainTooltip ( + containerColor = KotlinConfTheme.colors.tooltipBackground, + ) { + Text( + text = contentDescription, + color = KotlinConfTheme.colors.primaryTextInverted, + ) + } + }, state = rememberTooltipState() ) { Icon( diff --git a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/ColorValues.kt b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/ColorValues.kt index 57c4475d..064d953f 100644 --- a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/ColorValues.kt +++ b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/ColorValues.kt @@ -67,4 +67,8 @@ internal object UI { val greyLight = Color(0xFFA3A3A4) val greyDark = Color(0xFF757577) + + val blueGrey = Color(0xFF121318) + + val offWhite = Color(0xFFFAF8FF) } diff --git a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/Colors.kt b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/Colors.kt index 66d94ced..060771de 100644 --- a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/Colors.kt +++ b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/Colors.kt @@ -16,8 +16,10 @@ import org.jetbrains.kotlinconf.ui.theme.UI.black40 import org.jetbrains.kotlinconf.ui.theme.UI.black60 import org.jetbrains.kotlinconf.ui.theme.UI.black70 import org.jetbrains.kotlinconf.ui.theme.UI.black80 +import org.jetbrains.kotlinconf.ui.theme.UI.blueGrey import org.jetbrains.kotlinconf.ui.theme.UI.greyDark import org.jetbrains.kotlinconf.ui.theme.UI.greyLight +import org.jetbrains.kotlinconf.ui.theme.UI.offWhite import org.jetbrains.kotlinconf.ui.theme.UI.white05 import org.jetbrains.kotlinconf.ui.theme.UI.white10 import org.jetbrains.kotlinconf.ui.theme.UI.white100 @@ -55,6 +57,8 @@ class Colors( val toggleOn: Color, val toggleOff: Color, + + val tooltipBackground: Color, ) val KotlinConfLightColors = Colors( @@ -85,6 +89,8 @@ val KotlinConfLightColors = Colors( toggleOff = greyLight, toggleOn = magenta100, + + tooltipBackground = blueGrey, ) val KotlinConfDarkColors = Colors( @@ -109,10 +115,12 @@ val KotlinConfDarkColors = Colors( orangeText = orange, placeholderText = white30, primaryText = white100, - primaryTextInverted = white100, + primaryTextInverted = black100, purpleText = purpleTextDark, secondaryText = white70, toggleOff = greyDark, toggleOn = magentaTextDark, + + tooltipBackground = offWhite, ) From c185f9b377dead25ff0fcfb9f10f2ab3d7f61fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Gerlei?= Date: Tue, 11 Nov 2025 09:02:13 +0100 Subject: [PATCH 4/5] Use separate color for the Text in TooltipBox - Revert changing primaryTextInverted for dark theme (apparently, it was intentionally the same color as primaryText) --- .../kotlin/org/jetbrains/kotlinconf/ui/theme/Colors.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/Colors.kt b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/Colors.kt index 060771de..c5161cc0 100644 --- a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/Colors.kt +++ b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/Colors.kt @@ -59,6 +59,7 @@ class Colors( val toggleOff: Color, val tooltipBackground: Color, + val tooltipText: Color, ) val KotlinConfLightColors = Colors( @@ -91,6 +92,7 @@ val KotlinConfLightColors = Colors( toggleOn = magenta100, tooltipBackground = blueGrey, + tooltipText = white100, ) val KotlinConfDarkColors = Colors( @@ -115,7 +117,7 @@ val KotlinConfDarkColors = Colors( orangeText = orange, placeholderText = white30, primaryText = white100, - primaryTextInverted = black100, + primaryTextInverted = white100, purpleText = purpleTextDark, secondaryText = white70, @@ -123,4 +125,5 @@ val KotlinConfDarkColors = Colors( toggleOn = magentaTextDark, tooltipBackground = offWhite, + tooltipText = black100, ) From 8598ac94d4ff07aaa97c235bbbeea54201b16952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Braun?= Date: Tue, 11 Nov 2025 22:48:39 +0100 Subject: [PATCH 5/5] Use foundation Tooltip APIs, use existing color tokens temporarily --- .../kotlinconf/ui/components/TopMenuButton.kt | 62 +++++++++++++------ .../kotlinconf/ui/theme/ColorValues.kt | 4 -- .../jetbrains/kotlinconf/ui/theme/Colors.kt | 11 ---- 3 files changed, 43 insertions(+), 34 deletions(-) diff --git a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/TopMenuButton.kt b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/TopMenuButton.kt index 8210ff94..d5685d1d 100644 --- a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/TopMenuButton.kt +++ b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/TopMenuButton.kt @@ -1,20 +1,17 @@ package org.jetbrains.kotlinconf.ui.components import androidx.compose.animation.animateColorAsState +import androidx.compose.foundation.BasicTooltipBox +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberBasicTooltipState import androidx.compose.foundation.selection.toggleable import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.PlainTooltip -import androidx.compose.material3.TooltipAnchorPosition -import androidx.compose.material3.TooltipBox -import androidx.compose.material3.TooltipDefaults -import androidx.compose.material3.rememberTooltipState +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -23,9 +20,15 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.semantics.Role import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntRect +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.PopupPositionProvider import kotlinconfapp.ui_components.generated.resources.UiRes import kotlinconfapp.ui_components.generated.resources.bookmark_24 import kotlinconfapp.ui_components.generated.resources.close_24 @@ -35,7 +38,28 @@ import org.jetbrains.compose.resources.painterResource import org.jetbrains.kotlinconf.ui.theme.KotlinConfTheme import org.jetbrains.kotlinconf.ui.theme.PreviewHelper -@OptIn(ExperimentalMaterial3Api::class) + +@Composable +private fun rememberPositionProvider(): PopupPositionProvider { + val tooltipAnchorSpacing = with(LocalDensity.current) { 4.dp.roundToPx() } + return remember(tooltipAnchorSpacing) { + object : PopupPositionProvider { + override fun calculatePosition( + anchorBounds: IntRect, + windowSize: IntSize, + layoutDirection: LayoutDirection, + popupContentSize: IntSize, + ): IntOffset { + val x = anchorBounds.left + (anchorBounds.width - popupContentSize.width) / 2 + var y = anchorBounds.bottom - tooltipAnchorSpacing + if (y < 0) y = anchorBounds.bottom + tooltipAnchorSpacing + return IntOffset(x, y) + } + } + } +} + +@OptIn(ExperimentalFoundationApi::class) @Composable private fun TopMenuButtonImpl( icon: DrawableResource, @@ -45,19 +69,19 @@ private fun TopMenuButtonImpl( iconColor: Color, modifier: Modifier = Modifier, ) { - TooltipBox( - positionProvider = TooltipDefaults.rememberTooltipPositionProvider(TooltipAnchorPosition.Below), + BasicTooltipBox( + positionProvider = rememberPositionProvider(), tooltip = { - PlainTooltip ( - containerColor = KotlinConfTheme.colors.tooltipBackground, - ) { - Text( - text = contentDescription, - color = KotlinConfTheme.colors.primaryTextInverted, - ) - } + Text( + text = contentDescription, + color = KotlinConfTheme.colors.mainBackground, // TODO update per design + modifier = Modifier + .clip(RoundedCornerShape(4.dp)) + .background(KotlinConfTheme.colors.primaryText) // TODO update per design + .padding(4.dp) + ) }, - state = rememberTooltipState() + state = rememberBasicTooltipState() ) { Icon( modifier = modifier diff --git a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/ColorValues.kt b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/ColorValues.kt index 064d953f..57c4475d 100644 --- a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/ColorValues.kt +++ b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/ColorValues.kt @@ -67,8 +67,4 @@ internal object UI { val greyLight = Color(0xFFA3A3A4) val greyDark = Color(0xFF757577) - - val blueGrey = Color(0xFF121318) - - val offWhite = Color(0xFFFAF8FF) } diff --git a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/Colors.kt b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/Colors.kt index c5161cc0..66d94ced 100644 --- a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/Colors.kt +++ b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/theme/Colors.kt @@ -16,10 +16,8 @@ import org.jetbrains.kotlinconf.ui.theme.UI.black40 import org.jetbrains.kotlinconf.ui.theme.UI.black60 import org.jetbrains.kotlinconf.ui.theme.UI.black70 import org.jetbrains.kotlinconf.ui.theme.UI.black80 -import org.jetbrains.kotlinconf.ui.theme.UI.blueGrey import org.jetbrains.kotlinconf.ui.theme.UI.greyDark import org.jetbrains.kotlinconf.ui.theme.UI.greyLight -import org.jetbrains.kotlinconf.ui.theme.UI.offWhite import org.jetbrains.kotlinconf.ui.theme.UI.white05 import org.jetbrains.kotlinconf.ui.theme.UI.white10 import org.jetbrains.kotlinconf.ui.theme.UI.white100 @@ -57,9 +55,6 @@ class Colors( val toggleOn: Color, val toggleOff: Color, - - val tooltipBackground: Color, - val tooltipText: Color, ) val KotlinConfLightColors = Colors( @@ -90,9 +85,6 @@ val KotlinConfLightColors = Colors( toggleOff = greyLight, toggleOn = magenta100, - - tooltipBackground = blueGrey, - tooltipText = white100, ) val KotlinConfDarkColors = Colors( @@ -123,7 +115,4 @@ val KotlinConfDarkColors = Colors( toggleOff = greyDark, toggleOn = magentaTextDark, - - tooltipBackground = offWhite, - tooltipText = black100, )