Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ internal object JBPopupRenderer : PopupRenderer {
onPreviewKeyEvent: ((KeyEvent) -> Boolean)?,
onKeyEvent: ((KeyEvent) -> Boolean)?,
cornerSize: CornerSize,
windowShape: ((IntSize) -> java.awt.Shape)?,
content: @Composable () -> Unit,
) {
JBPopup(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ internal fun createBridgeComponentStyling(theme: ThemeDefinition): ComponentStyl
popupAdStyle = readPopupAdStyle(),
defaultSlimButtonStyle = readDefaultSlimButtonStyle(defaultButtonStyle.colors),
outlinedSlimButtonStyle = readOutlinedSlimButtonStyle(outlinedButtonStyle.colors),
gotItTooltipStyle = readGotItTooltipStyle(),
gotItButtonStyle = readGotItButtonStyle(),
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jewel.bridge.theme

import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import com.intellij.util.ui.JBUI
import org.jetbrains.jewel.bridge.dp
import org.jetbrains.jewel.bridge.retrieveColorOrUnspecified
import org.jetbrains.jewel.bridge.safeValue
import org.jetbrains.jewel.bridge.toComposeColor
import org.jetbrains.jewel.bridge.toPaddingValues
import org.jetbrains.jewel.foundation.Stroke
import org.jetbrains.jewel.ui.component.gotit.GotItColors
import org.jetbrains.jewel.ui.component.gotit.GotItMetrics
import org.jetbrains.jewel.ui.component.gotit.GotItTooltipStyle
import org.jetbrains.jewel.ui.component.styling.ButtonColors
import org.jetbrains.jewel.ui.component.styling.ButtonMetrics
import org.jetbrains.jewel.ui.component.styling.ButtonStyle

internal fun readGotItTooltipStyle(): GotItTooltipStyle =
GotItTooltipStyle(
colors =
GotItColors(
foreground = JBUI.CurrentTheme.GotItTooltip.foreground(false).toComposeColor(),
background = JBUI.CurrentTheme.GotItTooltip.background(false).toComposeColor(),
stepForeground = JBUI.CurrentTheme.GotItTooltip.stepForeground(false).toComposeColor(),
secondaryActionForeground =
JBUI.CurrentTheme.GotItTooltip.secondaryActionForeground(false).toComposeColor(),
headerForeground = JBUI.CurrentTheme.GotItTooltip.headerForeground(false).toComposeColor(),
balloonBorderColor = JBUI.CurrentTheme.GotItTooltip.borderColor(false).toComposeColor(),
imageBorderColor = JBUI.CurrentTheme.GotItTooltip.imageBorderColor(false).toComposeColor(),
link = JBUI.CurrentTheme.GotItTooltip.linkForeground(false).toComposeColor(),
codeForeground = JBUI.CurrentTheme.GotItTooltip.codeForeground(false).toComposeColor(),
codeBackground = JBUI.CurrentTheme.GotItTooltip.codeBackground(false).toComposeColor(),
),
metrics =
GotItMetrics(
contentPadding = JBUI.CurrentTheme.GotItTooltip.insets().toPaddingValues(),
textPadding = JBUI.CurrentTheme.GotItTooltip.TEXT_INSET.dp.safeValue(),
buttonPadding =
PaddingValues(
top = JBUI.CurrentTheme.GotItTooltip.BUTTON_TOP_INSET.dp.safeValue(),
bottom = JBUI.CurrentTheme.GotItTooltip.BUTTON_BOTTOM_INSET.dp.safeValue(),
),
iconPadding = JBUI.CurrentTheme.GotItTooltip.ICON_INSET.dp.safeValue(),
imagePadding =
PaddingValues(
top = JBUI.CurrentTheme.GotItTooltip.IMAGE_TOP_INSET.dp.safeValue(),
bottom = JBUI.CurrentTheme.GotItTooltip.IMAGE_BOTTOM_INSET.dp.safeValue(),
),
cornerRadius = JBUI.CurrentTheme.GotItTooltip.CORNER_RADIUS.dp.safeValue(),
),
)

internal fun readGotItButtonStyle(): ButtonStyle {
val background = SolidColor(retrieveColorOrUnspecified("GotItTooltip.Button.startBackground"))
val border = SolidColor(retrieveColorOrUnspecified("GotItTooltip.Button.startBorderColor"))
val content = JBUI.CurrentTheme.GotItTooltip.buttonForeground().toComposeColor()

return ButtonStyle(
colors =
ButtonColors(
background = background,
backgroundDisabled = SolidColor(Color.Unspecified),
backgroundFocused = background,
backgroundPressed = background,
backgroundHovered = background,
content = content,
contentDisabled = retrieveColorOrUnspecified("Button.disabledText"),
contentFocused = content,
contentPressed = content,
contentHovered = content,
border = border,
borderDisabled = SolidColor(Color.Unspecified),
borderFocused = border,
borderPressed = border,
borderHovered = border,
),
metrics =
ButtonMetrics(
cornerSize = CornerSize(JBUI.CurrentTheme.GotItTooltip.CORNER_RADIUS.dp.safeValue()),
padding = PaddingValues(horizontal = 12.dp, vertical = 6.dp),
minSize = DpSize(72.dp, 28.dp),
borderWidth = 1.dp,
focusOutlineExpand = 0.dp,
),
focusOutlineAlignment = Stroke.Alignment.Center,
)
}
37 changes: 35 additions & 2 deletions platform/jewel/int-ui/int-ui-standalone/api-dump.txt

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.semantics.popup
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.IntSize
Expand Down Expand Up @@ -102,6 +103,7 @@ internal object JDialogRenderer : PopupRenderer {
onPreviewKeyEvent: ((KeyEvent) -> Boolean)?,
onKeyEvent: ((KeyEvent) -> Boolean)?,
cornerSize: CornerSize,
windowShape: ((IntSize) -> java.awt.Shape)?,
content: @Composable () -> Unit,
) {
val isJBREnvironment = remember { JBR.isAvailable() && JBR.isRoundedCornersManagerSupported() }
Expand Down Expand Up @@ -148,6 +150,7 @@ internal object JDialogRenderer : PopupRenderer {
onKeyEvent = onKeyEvent,
cornerSize = cornerSize,
blendingEnabled = supportBlending,
windowShape = windowShape,
content = content,
)
}
Expand All @@ -164,6 +167,7 @@ private fun JPopupImpl(
onKeyEvent: ((KeyEvent) -> Boolean)?,
cornerSize: CornerSize,
blendingEnabled: Boolean,
windowShape: ((IntSize) -> java.awt.Shape)?,
content: @Composable () -> Unit,
) {
val popupDensity = LocalDensity.current
Expand All @@ -175,6 +179,7 @@ private fun JPopupImpl(
val currentOnKeyEvent by rememberUpdatedState(onKeyEvent)
val currentOnPreviewKeyEvent by rememberUpdatedState(onPreviewKeyEvent)
val currentProperties by rememberUpdatedState(properties)
val windowShapeState = rememberUpdatedState(windowShape)

val compositionLocalContext by rememberUpdatedState(currentCompositionLocalContext)

Expand Down Expand Up @@ -242,19 +247,40 @@ private fun JPopupImpl(
JPopupMeasurePolicy(dialog, currentPopupPositionProvider, parentBounds) { position, size ->
popupRectangle = Rectangle(position.x, position.y, size.width, size.height)

val currentWindowShape = windowShapeState.value
if (currentWindowShape != null) {
if (blendingEnabled) {
// When blending is active (via compose.interop.blending), the window is
// already in java.awt.GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSLUCENT
// mode (per-pixel alpha). Calling Window.setShape() would switch it to
// PERPIXEL_TRANSPARENT (hard pixel clip), breaking antialiasing at the edges.
// Compose's own drawing + transparent background is sufficient.
return@JPopupMeasurePolicy
}
// Without blending, fall back to Window.setShape() to at least
// clip the rectangular window boundary to the balloon outline.
// Note: this uses PERPIXEL_TRANSPARENT mode, which has known
// antialiasing limitations at concave corners (e.g. arrow junction).
val logicalSize =
IntSize(
floor(size.width / popupDensity.density).toInt(),
floor(size.height / popupDensity.density).toInt(),
)
try {
dialog.shape = currentWindowShape(logicalSize)
} catch (_: UnsupportedOperationException) {
applyRoundedCorners(dialog, cornerSize, size, popupDensity)
}
return@JPopupMeasurePolicy
}

if (blendingEnabled) {
// If any of the blending logic is enabled, we don't need to use JBR APIs
// to set the rounded corners and fix the background.
return@JPopupMeasurePolicy
}

if (cornerSize != ZeroCornerSize) {
JBR.getRoundedCornersManager()
.setRoundedCorners(
dialog,
cornerSize.toPx(size.toSize(), popupDensity) / dialog.density(),
)
}
applyRoundedCorners(dialog, cornerSize, size, popupDensity)
}
},
)
Expand Down Expand Up @@ -390,6 +416,11 @@ private class JPopupMeasurePolicy(
}
}

private fun applyRoundedCorners(dialog: Window, cornerSize: CornerSize, size: IntSize, density: Density) {
if (cornerSize == ZeroCornerSize) return
JBR.getRoundedCornersManager().setRoundedCorners(dialog, cornerSize.toPx(size.toSize(), density) / dialog.density())
}

// Based on implementation from JBUIScale and ScreenUtil
private fun IntSize.Companion.screenSize(window: Component): IntSize {
val windowConfiguration = window.graphicsConfiguration.device.defaultConfiguration
Expand Down
Loading
Loading