Skip to content

Commit

Permalink
Make tooltip placement same as IJ
Browse files Browse the repository at this point in the history
  • Loading branch information
devkanro committed Oct 13, 2023
1 parent 1d0241c commit 7a228de
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 7 deletions.
119 changes: 112 additions & 7 deletions core/src/main/kotlin/org/jetbrains/jewel/Tooltip.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,34 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
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 org.jetbrains.jewel.styling.TooltipStyle

@Composable fun Tooltip(
@Composable
fun Tooltip(
tooltip: @Composable () -> Unit,
modifier: Modifier = Modifier,
tooltipPlacement: TooltipPlacement = TooltipPlacement.ComponentRect(
alignment = Alignment.CenterEnd,
anchor = Alignment.BottomEnd,
offset = DpOffset(4.dp, 4.dp),
),
style: TooltipStyle = IntelliJTheme.tooltipStyle,
tooltipPlacement: TooltipPlacement = IntellijTooltipPlacement(
contentOffset = style.metrics.tooltipOffset,
alignment = style.metrics.tooltipAlignment,
density = LocalDensity.current,
),
content: @Composable () -> Unit,
) {
TooltipArea(
Expand All @@ -52,7 +63,9 @@ import org.jetbrains.jewel.styling.TooltipStyle
)
.padding(style.metrics.contentPadding),
) {
tooltip()
onBackground(style.colors.background) {
tooltip()
}
}
}
},
Expand All @@ -62,3 +75,95 @@ import org.jetbrains.jewel.styling.TooltipStyle
content = content,
)
}

class IntellijTooltipPlacement(
private val contentOffset: DpOffset,
private val alignment: Alignment.Horizontal,
private val density: Density,
private val windowMargin: Dp = 4.dp,
) : TooltipPlacement {

@Composable
@Suppress("OVERRIDE_DEPRECATION")
override fun positionProvider(): PopupPositionProvider {
error("Not supported")
}

@Composable
override fun positionProvider(cursorPosition: Offset): PopupPositionProvider {
return rememberIntellijTooltipPositionProvider(
cursorPosition = cursorPosition,
contentOffset = contentOffset,
alignment = alignment,
density = density,
windowMargin = windowMargin,
)
}
}

@Composable
private fun rememberIntellijTooltipPositionProvider(
cursorPosition: Offset,
contentOffset: DpOffset,
alignment: Alignment.Horizontal,
density: Density,
windowMargin: Dp = 4.dp,
) = remember(
contentOffset,
alignment,
density,
windowMargin,
) {
IntellijTooltipPositionProvider(
cursorPosition = cursorPosition,
contentOffset = contentOffset,
alignment = alignment,
density = density,
windowMargin = windowMargin,
)
}

private class IntellijTooltipPositionProvider(
private val cursorPosition: Offset,
private val contentOffset: DpOffset,
private val alignment: Alignment.Horizontal,
private val density: Density,
private val windowMargin: Dp = 4.dp,
) : PopupPositionProvider {

override fun calculatePosition(
anchorBounds: IntRect,
windowSize: IntSize,
layoutDirection: LayoutDirection,
popupContentSize: IntSize,
): IntOffset = with(density) {
val windowSpaceBounds = IntRect(
left = windowMargin.roundToPx(),
top = windowMargin.roundToPx(),
right = windowSize.width - windowMargin.roundToPx(),
bottom = windowSize.height - windowMargin.roundToPx(),
)

val contentOffsetX = contentOffset.x.roundToPx()
val contentOffsetY = contentOffset.y.roundToPx()

val posX = cursorPosition.x.toInt() + anchorBounds.left
val posY = cursorPosition.y.toInt() + anchorBounds.top

val x = posX + alignment.align(popupContentSize.width, 0, layoutDirection) + contentOffsetX

val aboveSpacing = cursorPosition.y - contentOffsetY - windowSpaceBounds.top
val belowSpacing = windowSpaceBounds.bottom - cursorPosition.y - contentOffsetY

val y = if (belowSpacing > popupContentSize.height || belowSpacing >= aboveSpacing) {
posY + contentOffsetY
} else {
posY - contentOffsetY - popupContentSize.height
}

val popupBounds = IntRect(x, y, x + popupContentSize.width, y + popupContentSize.height)
.constrainedIn(windowSpaceBounds)

IntOffset(popupBounds.left, popupBounds.top)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.runtime.Stable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
import kotlin.time.Duration

@Stable
Expand All @@ -32,6 +34,9 @@ interface TooltipMetrics {
val cornerSize: CornerSize
val borderWidth: Dp
val shadowSize: Dp

val tooltipOffset: DpOffset
val tooltipAlignment: Alignment.Horizontal
}

val LocalTooltipStyle = staticCompositionLocalOf<TooltipStyle> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import org.jetbrains.jewel.intui.core.theme.IntUiDarkTheme
import org.jetbrains.jewel.intui.core.theme.IntUiLightTheme
Expand Down Expand Up @@ -78,4 +80,6 @@ data class IntUiTooltipMetrics(
override val cornerSize: CornerSize = CornerSize(5.dp),
override val borderWidth: Dp = 1.dp,
override val shadowSize: Dp = 12.dp,
override val tooltipOffset: DpOffset = DpOffset(0.dp, 20.dp),
override val tooltipAlignment: Alignment.Horizontal = Alignment.Start,
) : TooltipMetrics

0 comments on commit 7a228de

Please sign in to comment.