Skip to content

Commit

Permalink
Fix icon loading and errors in bridge for 241
Browse files Browse the repository at this point in the history
  • Loading branch information
rock3r committed Feb 1, 2024
1 parent eb0ed11 commit 27f1d6b
Show file tree
Hide file tree
Showing 36 changed files with 416 additions and 214 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,60 @@ import com.intellij.ide.ui.UITheme
import com.intellij.openapi.diagnostic.thisLogger
import org.jetbrains.jewel.foundation.InternalJewelApi
import org.jetbrains.jewel.foundation.theme.JewelTheme
import org.jetbrains.jewel.ui.painter.BasePainterHintsProvider
import org.jetbrains.jewel.ui.painter.PalettePainterHintsProvider
import org.jetbrains.jewel.ui.painter.PainterHint
import org.jetbrains.jewel.ui.painter.hints.ColorBasedPaletteReplacement
import org.jetbrains.jewel.ui.painter.hints.Dark
import org.jetbrains.jewel.ui.painter.hints.HiDpi
import org.jetbrains.jewel.ui.util.fromRGBAHexStringOrNull

@InternalJewelApi
public class BridgePainterHintsProvider private constructor(
isDark: Boolean,
intellijIconPalette: Map<String, String?> = emptyMap(),
themeIconPalette: Map<String, String?> = emptyMap(),
themeColorPalette: Map<String, Color?> = emptyMap(),
) : BasePainterHintsProvider(isDark, intellijIconPalette, themeIconPalette, themeColorPalette) {
) : PalettePainterHintsProvider(isDark, intellijIconPalette, themeIconPalette, themeColorPalette) {

override val checkBoxPaletteHint: PainterHint
override val treePaletteHint: PainterHint
override val uiPaletteHint: PainterHint

init {
val ui = mutableMapOf<Color, Color>()
val checkBoxes = mutableMapOf<Color, Color>()
val trees = mutableMapOf<Color, Color>()

@Suppress("LoopWithTooManyJumpStatements")
for ((key, value) in themeIconPalette) {
if (value == null) continue
val map = selectMap(key, checkBoxes, trees, ui) ?: continue

// If either the key or the resolved value aren't valid colors, ignore the entry
val keyAsColor = resolveKeyColor(key, intellijIconPalette, isDark) ?: continue
val resolvedColor = resolveColor(value) ?: continue

// Save the new entry (oldColor -> newColor) in the map
map[keyAsColor] = resolvedColor
}

checkBoxPaletteHint = ColorBasedPaletteReplacement(checkBoxes)
treePaletteHint = ColorBasedPaletteReplacement(trees)
uiPaletteHint = ColorBasedPaletteReplacement(ui)
}

private fun selectMap(
key: String,
checkBoxes: MutableMap<Color, Color>,
trees: MutableMap<Color, Color>,
ui: MutableMap<Color, Color>,
) =
when {
key.startsWith("Checkbox.") -> checkBoxes
key.startsWith("Tree.iconColor.") -> trees
key.startsWith("Objects.") || key.startsWith("Actions.") || key.startsWith("#") -> ui
else -> null
}

@Composable
override fun hints(path: String): List<PainterHint> = buildList {
Expand All @@ -32,7 +74,7 @@ public class BridgePainterHintsProvider private constructor(
private val logger = thisLogger()

@Suppress("UnstableApiUsage") // We need to call @Internal APIs
public operator fun invoke(isDark: Boolean): BasePainterHintsProvider {
public operator fun invoke(isDark: Boolean): PalettePainterHintsProvider {
val uiTheme = currentUiThemeOrNull() ?: return BridgePainterHintsProvider(isDark)
logger.info("Parsing theme info from theme ${uiTheme.name} (id: ${uiTheme.id}, isDark: ${uiTheme.isDark})")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,13 @@ private fun readDefaultButtonStyle(): ButtonStyle {
borderHovered = normalBorder,
)

val minimumSize = JBUI.CurrentTheme.Button.minimumSize()
return ButtonStyle(
colors = colors,
metrics = ButtonMetrics(
cornerSize = retrieveArcAsCornerSizeWithFallbacks("Button.default.arc", "Button.arc"),
padding = PaddingValues(horizontal = 14.dp), // see DarculaButtonUI.HORIZONTAL_PADDING
minSize = DpSize(DarculaUIUtil.MINIMUM_WIDTH.dp, DarculaUIUtil.MINIMUM_HEIGHT.dp),
minSize = DpSize(minimumSize.width.dp, minimumSize.height.dp),
borderWidth = DarculaUIUtil.LW.dp,
),
)
Expand Down Expand Up @@ -264,13 +265,14 @@ private fun readOutlinedButtonStyle(): ButtonStyle {
borderHovered = normalBorder,
)

val minimumSize = JBUI.CurrentTheme.Button.minimumSize()
return ButtonStyle(
colors = colors,
metrics =
ButtonMetrics(
cornerSize = CornerSize(DarculaUIUtil.BUTTON_ARC.dp / 2),
padding = PaddingValues(horizontal = 14.dp), // see DarculaButtonUI.HORIZONTAL_PADDING
minSize = DpSize(DarculaUIUtil.MINIMUM_WIDTH.dp, DarculaUIUtil.MINIMUM_HEIGHT.dp),
minSize = DpSize(minimumSize.width.dp, minimumSize.height.dp),
borderWidth = DarculaUIUtil.LW.dp,
),
)
Expand All @@ -288,9 +290,10 @@ private fun readCheckboxStyle(): CheckboxStyle {
colors = colors,
metrics = CheckboxMetrics(
checkboxSize = DarculaCheckBoxUI().defaultIcon.let { DpSize(it.iconWidth.dp, it.iconHeight.dp) },
checkboxCornerSize = CornerSize(3.dp), // See DarculaCheckBoxUI#drawCheckIcon
outlineCornerSize = CornerSize(3.dp), // See DarculaCheckBoxUI#drawCheckIcon
outlineSelectedCornerSize = CornerSize(3.dp),
outlineSize = DpSize(15.dp, 15.dp), // Extrapolated from SVG
outlineOffset = DpOffset(2.5.dp, 1.5.dp), // Extrapolated from SVG
outlineSelectedSize = DpSize(16.dp, 16.dp), // Extrapolated from SVG
iconContentGap = 5.dp, // See DarculaCheckBoxUI#textIconGap
),
icons = CheckboxIcons(checkbox = bridgePainterProvider("${iconsBasePath}checkBox.svg")),
Expand Down Expand Up @@ -394,12 +397,13 @@ private fun readDefaultDropdownStyle(
iconTintHovered = Color.Unspecified,
)

val arrowWidth = DarculaUIUtil.ARROW_BUTTON_WIDTH.dp
val minimumSize = JBUI.CurrentTheme.ComboBox.minimumSize()
val arrowWidth = JBUI.CurrentTheme.Component.ARROW_AREA_WIDTH.dp
return DropdownStyle(
colors = colors,
metrics = DropdownMetrics(
arrowMinSize = DpSize(arrowWidth, DarculaUIUtil.MINIMUM_HEIGHT.dp),
minSize = DpSize(DarculaUIUtil.MINIMUM_WIDTH.dp + arrowWidth, DarculaUIUtil.MINIMUM_HEIGHT.dp),
arrowMinSize = DpSize(arrowWidth, minimumSize.height.dp),
minSize = DpSize(minimumSize.width.dp + arrowWidth, minimumSize.height.dp),
cornerSize = CornerSize(DarculaUIUtil.COMPONENT_ARC.dp),
contentPadding = retrieveInsetsAsPaddingValues("ComboBox.padding"),
borderWidth = DarculaUIUtil.BW.dp,
Expand Down Expand Up @@ -441,12 +445,14 @@ private fun readUndecoratedDropdownStyle(
iconTintHovered = Color.Unspecified,
)

val arrowWidth = DarculaUIUtil.ARROW_BUTTON_WIDTH.dp
val arrowWidth = JBUI.CurrentTheme.Component.ARROW_AREA_WIDTH.dp
val minimumSize = JBUI.CurrentTheme.Button.minimumSize()

return DropdownStyle(
colors = colors,
metrics = DropdownMetrics(
arrowMinSize = DpSize(arrowWidth, DarculaUIUtil.MINIMUM_HEIGHT.dp),
minSize = DpSize(DarculaUIUtil.MINIMUM_WIDTH.dp + arrowWidth, DarculaUIUtil.MINIMUM_HEIGHT.dp),
arrowMinSize = DpSize(arrowWidth, minimumSize.height.dp),
minSize = DpSize(minimumSize.width.dp + arrowWidth, minimumSize.height.dp),
cornerSize = CornerSize(JBUI.CurrentTheme.MainToolbar.Dropdown.hoverArc().dp),
contentPadding = JBUI.CurrentTheme.MainToolbar.Dropdown.borderInsets().toPaddingValues(),
borderWidth = 0.dp,
Expand Down Expand Up @@ -612,7 +618,9 @@ private fun readRadioButtonStyle(): RadioButtonStyle {
return RadioButtonStyle(
colors = colors,
metrics = RadioButtonMetrics(
radioButtonSize = DpSize(19.dp, 19.dp),
radioButtonSize = DpSize(24.dp, 24.dp), // Extrapolated from SVG
outlineSize = DpSize(17.dp, 17.dp), // Extrapolated from SVG
outlineSelectedSize = DpSize(22.dp, 22.dp), // Extrapolated from SVG
iconContentGap = retrieveIntAsDpOrUnspecified("RadioButton.textIconGap")
.takeOrElse { 4.dp },
),
Expand Down Expand Up @@ -723,12 +731,13 @@ private fun readTextFieldStyle(textFieldStyle: TextStyle): TextFieldStyle {
placeholder = NamedColorUtil.getInactiveTextColor().toComposeColor(),
)

val minimumSize = JBUI.CurrentTheme.TextField.minimumSize()
return TextFieldStyle(
colors = colors,
metrics = TextFieldMetrics(
cornerSize = CornerSize(DarculaUIUtil.COMPONENT_ARC.dp),
contentPadding = PaddingValues(horizontal = 9.dp, vertical = 2.dp),
minSize = DpSize(DarculaUIUtil.MINIMUM_WIDTH.dp, DarculaUIUtil.MINIMUM_HEIGHT.dp),
minSize = DpSize(minimumSize.width.dp, minimumSize.height.dp),
borderWidth = DarculaUIUtil.LW.dp,
),
textStyle = textFieldStyle,
Expand Down
3 changes: 1 addition & 2 deletions int-ui/int-ui-standalone/api/int-ui-standalone.api
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public final class org/jetbrains/jewel/intui/standalone/PainterProviderKt {
public static final fun standalonePainterProvider (Ljava/lang/String;)Lorg/jetbrains/jewel/ui/painter/ResourcePainterProvider;
}

public final class org/jetbrains/jewel/intui/standalone/StandalonePainterHintsProvider : org/jetbrains/jewel/ui/painter/BasePainterHintsProvider {
public final class org/jetbrains/jewel/intui/standalone/StandalonePainterHintsProvider : org/jetbrains/jewel/ui/painter/PalettePainterHintsProvider {
public static final field $stable I
public static final field Companion Lorg/jetbrains/jewel/intui/standalone/StandalonePainterHintsProvider$Companion;
public fun <init> (Lorg/jetbrains/jewel/foundation/theme/ThemeDefinition;)V
Expand Down Expand Up @@ -340,4 +340,3 @@ public final class org/jetbrains/jewel/intui/standalone/theme/IntUiThemeKt {
public static final fun light (Lorg/jetbrains/jewel/ui/ComponentStyling;Lorg/jetbrains/jewel/ui/component/styling/CheckboxStyle;Lorg/jetbrains/jewel/ui/component/styling/ChipStyle;Lorg/jetbrains/jewel/ui/component/styling/CircularProgressStyle;Lorg/jetbrains/jewel/ui/component/styling/ButtonStyle;Lorg/jetbrains/jewel/ui/component/styling/TabStyle;Lorg/jetbrains/jewel/ui/component/styling/DividerStyle;Lorg/jetbrains/jewel/ui/component/styling/DropdownStyle;Lorg/jetbrains/jewel/ui/component/styling/TabStyle;Lorg/jetbrains/jewel/ui/component/styling/GroupHeaderStyle;Lorg/jetbrains/jewel/ui/component/styling/HorizontalProgressBarStyle;Lorg/jetbrains/jewel/ui/component/styling/IconButtonStyle;Lorg/jetbrains/jewel/ui/component/styling/LazyTreeStyle;Lorg/jetbrains/jewel/ui/component/styling/LinkStyle;Lorg/jetbrains/jewel/ui/component/styling/MenuStyle;Lorg/jetbrains/jewel/ui/component/styling/ButtonStyle;Lorg/jetbrains/jewel/ui/component/styling/RadioButtonStyle;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;Lorg/jetbrains/jewel/ui/component/styling/SliderStyle;Lorg/jetbrains/jewel/ui/component/styling/TextAreaStyle;Lorg/jetbrains/jewel/ui/component/styling/TextFieldStyle;Lorg/jetbrains/jewel/ui/component/styling/TooltipStyle;Lorg/jetbrains/jewel/ui/component/styling/DropdownStyle;Landroidx/compose/runtime/Composer;IIII)Lorg/jetbrains/jewel/ui/ComponentStyling;
public static final fun lightThemeDefinition-RFMEUTM (Lorg/jetbrains/jewel/foundation/theme/JewelTheme$Companion;Lorg/jetbrains/jewel/foundation/GlobalColors;Lorg/jetbrains/jewel/foundation/GlobalMetrics;Lorg/jetbrains/jewel/foundation/theme/ThemeColorPalette;Lorg/jetbrains/jewel/foundation/theme/ThemeIconData;Landroidx/compose/ui/text/TextStyle;JLandroidx/compose/runtime/Composer;II)Lorg/jetbrains/jewel/foundation/theme/ThemeDefinition;
}

Original file line number Diff line number Diff line change
@@ -1,30 +1,109 @@
package org.jetbrains.jewel.intui.standalone

import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import org.jetbrains.jewel.foundation.theme.JewelTheme
import org.jetbrains.jewel.foundation.theme.ThemeDefinition
import org.jetbrains.jewel.ui.painter.BasePainterHintsProvider
import org.jetbrains.jewel.ui.painter.PainterHint
import org.jetbrains.jewel.ui.painter.PalettePainterHintsProvider
import org.jetbrains.jewel.ui.painter.hints.ColorBasedPaletteReplacement
import org.jetbrains.jewel.ui.painter.hints.Dark
import org.jetbrains.jewel.ui.painter.hints.HiDpi
import org.jetbrains.jewel.ui.painter.hints.KeyBasedPaletteReplacement
import org.jetbrains.jewel.ui.painter.hints.Override
import org.jetbrains.jewel.ui.util.inDebugMode

public class StandalonePainterHintsProvider(
theme: ThemeDefinition,
) : BasePainterHintsProvider(
) : PalettePainterHintsProvider(
theme.isDark,
intellijColorPalette,
theme.iconData.colorPalette,
theme.colorPalette.rawMap,
) {

override val checkBoxPaletteHint: PainterHint
override val treePaletteHint: PainterHint
override val uiPaletteHint: PainterHint

private val overrideHint: PainterHint =
Override(
theme.iconData.iconOverrides.entries.associate { (k, v) ->
k.removePrefix("/") to v.removePrefix("/")
},
)

init {
val ui = mutableMapOf<Color, Color>()
val checkBoxes = mutableMapOf<String, Color>()
val trees = mutableMapOf<Color, Color>()

@Suppress("LoopWithTooManyJumpStatements")
for ((key, value) in themeIconPalette) {
if (value == null) continue

// Checkbox (and radio button) entries work differently: the ID field
// for each element that needs patching has a "[fillKey]_[strokeKey]"
// format, starting from IJP 241.
if (key.startsWith("Checkbox.")) {
// Note: in the 241 bridge, we also need to check if the theme is New UI or not
registerIdBasedReplacement(checkBoxes, key, value)
} else {
val map = selectMap(key, trees, ui) ?: continue
registerColorBasedReplacement(map, key, value)
}
}

checkBoxPaletteHint = KeyBasedPaletteReplacement(checkBoxes)
treePaletteHint = ColorBasedPaletteReplacement(trees)
uiPaletteHint = ColorBasedPaletteReplacement(ui)
}

private fun selectMap(
key: String,
trees: MutableMap<Color, Color>,
ui: MutableMap<Color, Color>,
) =
when {
key.startsWith("Tree.iconColor.") -> trees
key.startsWith("Objects.") || key.startsWith("Actions.") || key.startsWith("#") -> ui
else -> null
}

private fun registerColorBasedReplacement(map: MutableMap<Color, Color>, key: String, value: String) {
// If either the key or the resolved value aren't valid colors, ignore the entry
val keyAsColor = resolveKeyColor(key, intellijIconPalette, isDark) ?: return
val resolvedColor = resolveColor(value) ?: return

// Save the new entry (oldColor -> newColor) in the map
map[keyAsColor] = resolvedColor
}

private fun registerIdBasedReplacement(map: MutableMap<String, Color>, key: String, value: String) {
val adjustedKey = if (isDark) key.removeSuffix(".Dark") else key

if (adjustedKey !in supportedCheckboxKeys) {
if (inDebugMode) {
println("${if (isDark) "Dark" else "Light"} theme: color key $key is not supported, will be ignored")
}
return
}

if (adjustedKey != key && inDebugMode) {
println("${if (isDark) "Dark" else "Light"} theme: color key $key is deprecated, use $adjustedKey instead")
}

val parsedValue = resolveColor(value)
if (parsedValue == null) {
if (inDebugMode) {
println("${if (isDark) "Dark" else "Light"} theme: color key $key has invalid value: '$value'")
}
return
}

map[adjustedKey] = parsedValue
}

@Composable
override fun hints(path: String): List<PainterHint> = buildList {
add(getPaletteHint(path))
Expand Down Expand Up @@ -86,5 +165,17 @@ public class StandalonePainterHintsProvider(
"Tree.iconColor" to "#808080",
"Tree.iconColor.Dark" to "#AFB1B3",
)

private val supportedCheckboxKeys: Set<String> = setOf(
"Checkbox.Background.Default",
"Checkbox.Border.Default",
"Checkbox.Foreground.Selected",
"Checkbox.Background.Selected",
"Checkbox.Border.Selected",
"Checkbox.Focus.Wide",
"Checkbox.Foreground.Disabled",
"Checkbox.Background.Disabled",
"Checkbox.Border.Disabled"
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import androidx.compose.foundation.shape.CornerSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import org.jetbrains.jewel.foundation.Stroke
import org.jetbrains.jewel.intui.core.theme.IntUiDarkTheme
import org.jetbrains.jewel.intui.core.theme.IntUiLightTheme
import org.jetbrains.jewel.intui.standalone.standalonePainterProvider
Expand Down Expand Up @@ -49,17 +49,19 @@ public fun CheckboxColors.Companion.dark(
CheckboxColors(content, contentDisabled, contentSelected)

public fun CheckboxMetrics.Companion.defaults(
checkboxSize: DpSize = DpSize(19.dp, 19.dp),
checkboxCornerSize: CornerSize = CornerSize(3.dp),
outlineSize: DpSize = DpSize(15.dp, 15.dp),
outlineOffset: DpOffset = DpOffset(2.5.dp, 1.5.dp),
checkboxSize: DpSize = DpSize(24.dp, 24.dp),
outlineCornerSize: CornerSize = CornerSize(3.dp),
outlineSelectedCornerSize: CornerSize = CornerSize(4.5.dp),
outlineSize: DpSize = DpSize(16.dp, 16.dp),
outlineSelectedSize: DpSize = DpSize(20.dp, 20.dp),
iconContentGap: Dp = 5.dp,
): CheckboxMetrics =
CheckboxMetrics(
checkboxSize,
checkboxCornerSize,
outlineCornerSize,
outlineSelectedCornerSize,
outlineSize,
outlineOffset,
outlineSelectedSize,
iconContentGap,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,12 @@ public fun RadioButtonColors.Companion.dark(
)

public fun RadioButtonMetrics.Companion.defaults(
radioButtonSize: DpSize = DpSize(19.dp, 19.dp),
radioButtonSize: DpSize = DpSize(24.dp, 24.dp),
outlineSize: DpSize = DpSize(17.dp, 17.dp),
outlineSelectedSize: DpSize = DpSize(22.dp, 22.dp),
iconContentGap: Dp = 8.dp,
): RadioButtonMetrics =
RadioButtonMetrics(radioButtonSize, iconContentGap)
RadioButtonMetrics(radioButtonSize, outlineSize, outlineSelectedSize, iconContentGap)

public fun RadioButtonIcons.Companion.light(
radioButton: PainterProvider =
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 27f1d6b

Please sign in to comment.