mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 14:23:28 +07:00
Fix 242 icon loading by providing new key-based API (#430)
* Implement icon keys generator This gradle task generates the icon keys for the platform icons that we'll need to load icons from a key instead of a path. * Define IconIdInterpreter and NewUiChecker * Do icon key generation for AllIcons in ui module * Implement proper key-based icon loading * Remove old TODO and unused code * Undo change to run config * Rename AllIcons to PlatformIcon, move to its own file * Remove unnecessary suppression * Run apiDump * Made icon generation more maintainable (#432) * Made icon generation more maintainable * Cleanup code * Fix ktlint implicitly trying to check generated code --------- Co-authored-by: Sebastiano Poggi <sebp@google.com> --------- Co-authored-by: Lamberto Basti <basti.lamberto@gmail.com> GitOrigin-RevId: bb5da85d239a72cc907c6edfe873f96629c6e9d2
This commit is contained in:
committed by
intellij-monorepo-bot
parent
b412b503e2
commit
d17fbb5d0f
3
platform/jewel/.gitignore
vendored
3
platform/jewel/.gitignore
vendored
@@ -4,9 +4,6 @@
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<inspection_tool class="LanguageDetectionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="NoButtonGroup" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="NoScrollPane" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="StructuralWrap" enabled="true" level="INFORMATION" enabled_by_default="true" />
|
||||
<inspection_tool class="XsltDeclarations" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="XsltUnusedDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="XsltVariableShadowing" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
|
||||
@@ -24,6 +24,7 @@ dependencies {
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.poko.gradlePlugin)
|
||||
|
||||
|
||||
// Enables using type-safe accessors to reference plugins from the [plugins] block defined in
|
||||
// version catalogs.
|
||||
// Context: https://github.com/gradle/gradle/issues/15383#issuecomment-779893192
|
||||
|
||||
@@ -8,6 +8,9 @@ dependencyResolutionManagement {
|
||||
repositories {
|
||||
google()
|
||||
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
|
||||
maven("https://www.jetbrains.com/intellij-repository/releases")
|
||||
maven("https://www.jetbrains.com/intellij-repository/snapshots")
|
||||
maven("https://cache-redirector.jetbrains.com/intellij-dependencies")
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,333 @@
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
import com.squareup.kotlinpoet.ClassName
|
||||
import com.squareup.kotlinpoet.FileSpec
|
||||
import com.squareup.kotlinpoet.PropertySpec
|
||||
import com.squareup.kotlinpoet.TypeSpec
|
||||
import io.gitlab.arturbosch.detekt.Detekt
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonNull
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.tasks.BaseKotlinCompile
|
||||
import java.lang.reflect.Field
|
||||
import java.net.URLClassLoader
|
||||
|
||||
private val defaultOutputDir: Provider<Directory> = layout.buildDirectory.dir("generated/iconKeys")
|
||||
|
||||
class IconKeysGeneratorContainer(
|
||||
container: NamedDomainObjectContainer<IconKeysGeneration>,
|
||||
) : NamedDomainObjectContainer<IconKeysGeneration> by container
|
||||
|
||||
class IconKeysGeneration(
|
||||
val name: String,
|
||||
project: Project,
|
||||
) {
|
||||
val outputDirectory: DirectoryProperty =
|
||||
project.objects
|
||||
.directoryProperty()
|
||||
.convention(defaultOutputDir)
|
||||
|
||||
val sourceClassName: Property<String> = project.objects.property<String>()
|
||||
val generatedClassName: Property<String> = project.objects.property<String>()
|
||||
}
|
||||
|
||||
val iconGeneration by configurations.registering {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
val extension = IconKeysGeneratorContainer(container<IconKeysGeneration> { IconKeysGeneration(it, project) })
|
||||
|
||||
extensions.add("intelliJIconKeysGenerator", extension)
|
||||
|
||||
extension.all item@{
|
||||
val task =
|
||||
tasks.register<IconKeysGeneratorTask>("generate${name}Keys") task@{
|
||||
this@task.outputDirectory = this@item.outputDirectory
|
||||
this@task.sourceClassName = this@item.sourceClassName
|
||||
this@task.generatedClassName = this@item.generatedClassName
|
||||
configuration.from(iconGeneration)
|
||||
dependsOn(iconGeneration)
|
||||
}
|
||||
|
||||
tasks {
|
||||
withType<BaseKotlinCompile> { dependsOn(task) }
|
||||
withType<Detekt> { dependsOn(task) }
|
||||
}
|
||||
}
|
||||
|
||||
pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
|
||||
the<KotlinJvmProjectExtension>()
|
||||
.sourceSets["main"]
|
||||
.kotlin
|
||||
.srcDir(defaultOutputDir)
|
||||
}
|
||||
|
||||
open class IconKeysGeneratorTask : DefaultTask() {
|
||||
|
||||
@get:OutputDirectory
|
||||
val outputDirectory: DirectoryProperty = project.objects.directoryProperty()
|
||||
|
||||
@get:Input
|
||||
val sourceClassName = project.objects.property<String>()
|
||||
|
||||
@get:Input
|
||||
val generatedClassName = project.objects.property<String>()
|
||||
|
||||
@get:InputFiles
|
||||
val configuration: ConfigurableFileCollection = project.objects.fileCollection()
|
||||
|
||||
init {
|
||||
group = "jewel"
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun generate() {
|
||||
val guessedSourceClassName = sourceClassName
|
||||
.map { ClassName.bestGuess(it).canonicalName.replace('.', '/') + ".kt" }
|
||||
.get()
|
||||
|
||||
// The icons artifacts are loaded on the iconGeneration configuration's classpath,
|
||||
// so we need a classloader that can access these classes.
|
||||
val classLoader = createClassLoader()
|
||||
val sourceClass = classLoader.loadClass(sourceClassName.get())
|
||||
?: throw GradleException(
|
||||
"Unable to load ${sourceClassName.get()}. " +
|
||||
"Is the correct dependency declared on the iconGeneration configuration?"
|
||||
)
|
||||
|
||||
// Step 1) load icon mappings from JSON
|
||||
val mappingsJsonBytes =
|
||||
classLoader.getResourceAsStream("PlatformIconMappings.json")
|
||||
?.use { it.readAllBytes() }
|
||||
?: error("Icon mapping JSON not found")
|
||||
|
||||
val iconMappingJson =
|
||||
json.parseToJsonElement(mappingsJsonBytes.decodeToString())
|
||||
|
||||
// Step 2) Transform mappings to a map oldPath -> newPath
|
||||
val iconMapping = readIconMappingJson(iconMappingJson)
|
||||
logger.lifecycle("Icon mapping JSON read. It has ${iconMapping.size} entries")
|
||||
|
||||
// Step 3) Traverse sourceClass by using reflection, collecting all members
|
||||
// This step uses the mappings to add the new paths where available.
|
||||
val dummyIconClass = classLoader.loadClass("com.intellij.ui.DummyIconImpl")
|
||||
val pathField = dummyIconClass.getPathField()
|
||||
|
||||
val rootHolder = IconKeyHolder(sourceClass.simpleName)
|
||||
pathField.whileForcingAccessible {
|
||||
visitSourceClass(sourceClass, iconMapping, rootHolder, pathField, classLoader)
|
||||
}
|
||||
logger.lifecycle("Read icon keys from ${sourceClass.name}")
|
||||
|
||||
// Step 4) Generate output Kotlin file
|
||||
val fileSpec = generateKotlinCode(rootHolder)
|
||||
val directory = outputDirectory.get().asFile
|
||||
fileSpec.writeTo(directory)
|
||||
|
||||
logger.lifecycle("Written icon keys for $guessedSourceClassName into $directory")
|
||||
}
|
||||
|
||||
private fun createClassLoader(): URLClassLoader {
|
||||
val arrayOfURLs = configuration.files
|
||||
.map { it.toURI().toURL() }
|
||||
.toTypedArray()
|
||||
|
||||
return URLClassLoader(
|
||||
arrayOfURLs,
|
||||
IconKeysGeneratorTask::class.java.classLoader
|
||||
)
|
||||
}
|
||||
|
||||
private fun readIconMappingJson(rawMapping: JsonElement): Map<String, String> {
|
||||
val flattenedMappings = mutableMapOf<String, Set<String>>()
|
||||
|
||||
visitMapping(oldUiPath = "", node = rawMapping, map = flattenedMappings)
|
||||
|
||||
return flattenedMappings
|
||||
.flatMap { (newPath, oldPaths) ->
|
||||
oldPaths.map { oldPath -> oldPath to newPath }
|
||||
}.toMap()
|
||||
}
|
||||
|
||||
private fun visitMapping(
|
||||
oldUiPath: String,
|
||||
node: JsonElement,
|
||||
map: MutableMap<String, Set<String>>,
|
||||
) {
|
||||
when (node) {
|
||||
is JsonPrimitive -> {
|
||||
if (!node.isString) return
|
||||
map[oldUiPath] = setOf(node.content)
|
||||
}
|
||||
|
||||
is JsonArray -> {
|
||||
map[oldUiPath] =
|
||||
node
|
||||
.filterIsInstance<JsonPrimitive>()
|
||||
.filter { child -> child.isString }
|
||||
.map { it.content }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
is JsonObject -> {
|
||||
for ((key, value) in node.entries) {
|
||||
val childOldPath = if (oldUiPath.isNotEmpty()) "$oldUiPath/$key" else key
|
||||
visitMapping(oldUiPath = childOldPath, node = value, map = map)
|
||||
}
|
||||
}
|
||||
|
||||
JsonNull -> error("Null nodes not supported")
|
||||
}
|
||||
}
|
||||
|
||||
private fun Field.whileForcingAccessible(action: () -> Unit) {
|
||||
@Suppress("DEPRECATION")
|
||||
val wasAccessible = isAccessible
|
||||
isAccessible = true
|
||||
try {
|
||||
action()
|
||||
} finally {
|
||||
isAccessible = wasAccessible
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitSourceClass(
|
||||
sourceClass: Class<*>,
|
||||
iconMappings: Map<String, String>,
|
||||
parentHolder: IconKeyHolder,
|
||||
pathField: Field,
|
||||
classLoader: ClassLoader,
|
||||
) {
|
||||
for (child in sourceClass.declaredClasses) {
|
||||
val childName = "${parentHolder.name}.${child.simpleName}"
|
||||
val childHolder = IconKeyHolder(childName)
|
||||
parentHolder.holders += childHolder
|
||||
visitSourceClass(child, iconMappings, childHolder, pathField, classLoader)
|
||||
}
|
||||
parentHolder.holders.sortBy { it.name }
|
||||
|
||||
sourceClass.declaredFields
|
||||
.filter { it.type == javax.swing.Icon::class.java }
|
||||
.forEach { field ->
|
||||
val fieldName = "${parentHolder.name}.${field.name}"
|
||||
|
||||
if (field.annotations.isNotEmpty()) {
|
||||
logger.lifecycle(
|
||||
"$fieldName -> ${field.annotations.joinToString { it!!.annotationClass.qualifiedName!! }}",
|
||||
)
|
||||
}
|
||||
|
||||
if (field.annotations.any { it.annotationClass == java.lang.Deprecated::class }) {
|
||||
logger.lifecycle("Ignoring deprecated field: $fieldName")
|
||||
return
|
||||
}
|
||||
|
||||
val icon = field.get(sourceClass)
|
||||
val oldPath = pathField.get(icon) as String
|
||||
|
||||
val newPath = iconMappings[oldPath]
|
||||
validatePath(oldPath, fieldName, classLoader)
|
||||
newPath?.let { validatePath(it, fieldName, classLoader) }
|
||||
parentHolder.keys += IconKey(fieldName, oldPath, newPath)
|
||||
}
|
||||
parentHolder.keys.sortBy { it.name }
|
||||
}
|
||||
|
||||
private fun validatePath(
|
||||
path: String,
|
||||
fieldName: String,
|
||||
classLoader: ClassLoader,
|
||||
) {
|
||||
val iconsClass = classLoader.loadClass(sourceClassName.get())
|
||||
if (iconsClass.getResourceAsStream("/${path.trimStart('/')}") == null) {
|
||||
logger.warn("Icon $fieldName: '$path' does not exist")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateKotlinCode(rootHolder: IconKeyHolder): FileSpec {
|
||||
val className = ClassName.bestGuess(generatedClassName.get())
|
||||
|
||||
return FileSpec
|
||||
.builder(className)
|
||||
.apply {
|
||||
indent(" ")
|
||||
addFileComment("Generated by the Jewel icon keys generator\n")
|
||||
addFileComment("Source class: ${sourceClassName.get()}")
|
||||
|
||||
addImport(keyClassName.packageName, keyClassName.simpleName)
|
||||
|
||||
val objectName = ClassName.bestGuess(generatedClassName.get())
|
||||
addType(
|
||||
TypeSpec
|
||||
.objectBuilder(objectName)
|
||||
.apply {
|
||||
for (childHolder in rootHolder.holders) {
|
||||
generateKotlinCodeInner(childHolder)
|
||||
}
|
||||
|
||||
for (key in rootHolder.keys) {
|
||||
addProperty(
|
||||
PropertySpec
|
||||
.builder(key.name.substringAfterLast('.'), keyClassName)
|
||||
.initializer(
|
||||
"%L",
|
||||
"""IntelliJIconKey("${key.oldPath}", "${key.newPath ?: key.oldPath}")""",
|
||||
).build(),
|
||||
)
|
||||
}
|
||||
}.build(),
|
||||
)
|
||||
}.build()
|
||||
}
|
||||
|
||||
private fun TypeSpec.Builder.generateKotlinCodeInner(holder: IconKeyHolder) {
|
||||
val objectName = holder.name.substringAfterLast('.')
|
||||
addType(
|
||||
TypeSpec
|
||||
.objectBuilder(objectName)
|
||||
.apply {
|
||||
for (childHolder in holder.holders) {
|
||||
generateKotlinCodeInner(childHolder)
|
||||
}
|
||||
|
||||
for (key in holder.keys) {
|
||||
addProperty(
|
||||
PropertySpec
|
||||
.builder(key.name.substringAfterLast('.'), keyClassName)
|
||||
.initializer(
|
||||
"%L",
|
||||
"""IntelliJIconKey("${key.oldPath}", "${key.newPath ?: key.oldPath}")"""
|
||||
)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
}.build(),
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private fun Class<*>.getPathField(): Field = declaredFields.first { it.name == "path" }
|
||||
|
||||
private val keyClassName = ClassName("org.jetbrains.jewel.ui.icon", "IntelliJIconKey")
|
||||
|
||||
private val json = Json { isLenient = true }
|
||||
}
|
||||
}
|
||||
|
||||
private data class IconKeyHolder(
|
||||
val name: String,
|
||||
val holders: MutableList<IconKeyHolder> = mutableListOf(),
|
||||
val keys: MutableList<IconKey> = mutableListOf(),
|
||||
)
|
||||
|
||||
private data class IconKey(
|
||||
val name: String,
|
||||
val oldPath: String,
|
||||
val newPath: String?,
|
||||
)
|
||||
@@ -25,7 +25,7 @@ class ThemeGeneration(val name: String, project: Project) {
|
||||
project.objects
|
||||
.directoryProperty()
|
||||
.convention(project.layout.buildDirectory.dir("generated/theme"))
|
||||
val ideaVersion = project.objects.property<String>().convention("232.6734")
|
||||
val ideaVersion = project.objects.property<String>()
|
||||
val themeClassName = project.objects.property<String>()
|
||||
val themeFile = project.objects.property<String>()
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@ kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serializa
|
||||
|
||||
jna-core = { module = "net.java.dev.jna:jna", version.ref = "jna" }
|
||||
|
||||
intellijPlatform-util-ui = { module = "com.jetbrains.intellij.platform:util-ui", version.ref = "idea" }
|
||||
intellijPlatform-icons = { module = "com.jetbrains.intellij.platform:icons", version.ref = "idea" }
|
||||
|
||||
# Plugin libraries for build-logic's convention plugins to use to resolve the types/tasks coming from these plugins
|
||||
detekt-gradlePlugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" }
|
||||
dokka-gradlePlugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" }
|
||||
|
||||
@@ -87,6 +87,10 @@ public final class org/jetbrains/jewel/bridge/actionSystem/ProvideDataKt {
|
||||
public static final fun provideData (Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;)Landroidx/compose/ui/Modifier;
|
||||
}
|
||||
|
||||
public final class org/jetbrains/jewel/bridge/icon/IntelliJIconKeyKt {
|
||||
public static final fun fromPlatformIcon (Lorg/jetbrains/jewel/ui/icon/IntelliJIconKey$Companion;Ljavax/swing/Icon;)Lorg/jetbrains/jewel/ui/icon/IconKey;
|
||||
}
|
||||
|
||||
public final class org/jetbrains/jewel/bridge/theme/BridgeGlobalColorsKt {
|
||||
public static final fun readFromLaF (Lorg/jetbrains/jewel/foundation/BorderColors$Companion;)Lorg/jetbrains/jewel/foundation/BorderColors;
|
||||
public static final fun readFromLaF (Lorg/jetbrains/jewel/foundation/GlobalColors$Companion;)Lorg/jetbrains/jewel/foundation/GlobalColors;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.jetbrains.jewel.bridge.icon
|
||||
|
||||
import org.jetbrains.jewel.bridge.isNewUiTheme
|
||||
import org.jetbrains.jewel.ui.icon.NewUiChecker
|
||||
|
||||
internal object BridgeNewUiChecker : NewUiChecker {
|
||||
override fun isNewUi(): Boolean = isNewUiTheme()
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.jetbrains.jewel.bridge.icon
|
||||
|
||||
import com.intellij.ui.icons.CachedImageIcon
|
||||
import org.jetbrains.jewel.ui.icon.IconKey
|
||||
import org.jetbrains.jewel.ui.icon.IntelliJIconKey
|
||||
|
||||
@Suppress("UnstableApiUsage") // We need to use internal APIs
|
||||
public fun IntelliJIconKey.Companion.fromPlatformIcon(icon: javax.swing.Icon): IconKey {
|
||||
check(icon is CachedImageIcon) {
|
||||
"Only resource-backed CachedImageIcons are supported (e.g., coming from AllIcons)"
|
||||
}
|
||||
|
||||
val oldUiPath =
|
||||
checkNotNull(icon.originalPath) {
|
||||
"Only resource-backed CachedImageIcons are supported (e.g., coming from AllIcons)"
|
||||
}
|
||||
|
||||
val newUiPath = icon.expUIPath ?: oldUiPath
|
||||
return IntelliJIconKey(oldUiPath, newUiPath)
|
||||
}
|
||||
@@ -8,9 +8,11 @@ import androidx.compose.ui.platform.LocalDensity
|
||||
import com.intellij.openapi.components.service
|
||||
import org.jetbrains.jewel.bridge.BridgePainterHintsProvider
|
||||
import org.jetbrains.jewel.bridge.SwingBridgeService
|
||||
import org.jetbrains.jewel.bridge.icon.BridgeNewUiChecker
|
||||
import org.jetbrains.jewel.bridge.scaleDensityWithIdeScale
|
||||
import org.jetbrains.jewel.foundation.ExperimentalJewelApi
|
||||
import org.jetbrains.jewel.ui.ComponentStyling
|
||||
import org.jetbrains.jewel.ui.icon.LocalNewUiChecker
|
||||
import org.jetbrains.jewel.ui.painter.LocalPainterHintsProvider
|
||||
import org.jetbrains.jewel.ui.theme.BaseJewelTheme
|
||||
|
||||
@@ -29,6 +31,7 @@ public fun SwingBridgeTheme(content: @Composable () -> Unit) {
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalPainterHintsProvider provides BridgePainterHintsProvider(themeData.themeDefinition.isDark),
|
||||
LocalNewUiChecker provides BridgeNewUiChecker,
|
||||
LocalDensity provides scaleDensityWithIdeScale(LocalDensity.current),
|
||||
) {
|
||||
content()
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.jetbrains.jewel.intui.standalone.icon
|
||||
|
||||
import org.jetbrains.jewel.ui.icon.NewUiChecker
|
||||
|
||||
internal object StandaloneNewUiChecker : NewUiChecker {
|
||||
override fun isNewUi(): Boolean = true
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import org.jetbrains.jewel.foundation.theme.ThemeIconData
|
||||
import org.jetbrains.jewel.intui.core.theme.IntUiDarkTheme
|
||||
import org.jetbrains.jewel.intui.core.theme.IntUiLightTheme
|
||||
import org.jetbrains.jewel.intui.standalone.StandalonePainterHintsProvider
|
||||
import org.jetbrains.jewel.intui.standalone.icon.StandaloneNewUiChecker
|
||||
import org.jetbrains.jewel.intui.standalone.styling.Default
|
||||
import org.jetbrains.jewel.intui.standalone.styling.Editor
|
||||
import org.jetbrains.jewel.intui.standalone.styling.Outlined
|
||||
@@ -40,6 +41,7 @@ import org.jetbrains.jewel.ui.component.styling.TabStyle
|
||||
import org.jetbrains.jewel.ui.component.styling.TextAreaStyle
|
||||
import org.jetbrains.jewel.ui.component.styling.TextFieldStyle
|
||||
import org.jetbrains.jewel.ui.component.styling.TooltipStyle
|
||||
import org.jetbrains.jewel.ui.icon.LocalNewUiChecker
|
||||
import org.jetbrains.jewel.ui.painter.LocalPainterHintsProvider
|
||||
import org.jetbrains.jewel.ui.theme.BaseJewelTheme
|
||||
|
||||
@@ -233,6 +235,7 @@ public fun IntUiTheme(
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalPainterHintsProvider provides StandalonePainterHintsProvider(theme),
|
||||
LocalNewUiChecker provides StandaloneNewUiChecker,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package icons
|
||||
|
||||
import org.jetbrains.jewel.ui.icon.IntelliJIconKey
|
||||
|
||||
object IdeSampleIconKeys {
|
||||
val gitHub = IntelliJIconKey("icons/github.svg", "icons/github.svg")
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
@@ -30,7 +31,7 @@ import com.intellij.icons.AllIcons
|
||||
import com.intellij.ide.BrowserUtil
|
||||
import com.intellij.ui.JBColor
|
||||
import com.intellij.util.ui.JBUI
|
||||
import icons.JewelIcons
|
||||
import icons.IdeSampleIconKeys
|
||||
import org.jetbrains.jewel.bridge.LocalComponent
|
||||
import org.jetbrains.jewel.bridge.toComposeColor
|
||||
import org.jetbrains.jewel.foundation.lazy.tree.buildTree
|
||||
@@ -53,6 +54,7 @@ import org.jetbrains.jewel.ui.component.Icon
|
||||
import org.jetbrains.jewel.ui.component.IconButton
|
||||
import org.jetbrains.jewel.ui.component.LazyTree
|
||||
import org.jetbrains.jewel.ui.component.OutlinedButton
|
||||
import org.jetbrains.jewel.ui.component.PlatformIcon
|
||||
import org.jetbrains.jewel.ui.component.RadioButtonRow
|
||||
import org.jetbrains.jewel.ui.component.Slider
|
||||
import org.jetbrains.jewel.ui.component.Text
|
||||
@@ -60,6 +62,12 @@ import org.jetbrains.jewel.ui.component.TextField
|
||||
import org.jetbrains.jewel.ui.component.Tooltip
|
||||
import org.jetbrains.jewel.ui.component.Typography
|
||||
import org.jetbrains.jewel.ui.component.separator
|
||||
import org.jetbrains.jewel.ui.icons.AllIconsKeys
|
||||
import org.jetbrains.jewel.ui.painter.badge.DotBadgeShape
|
||||
import org.jetbrains.jewel.ui.painter.hints.Badge
|
||||
import org.jetbrains.jewel.ui.painter.hints.Size
|
||||
import org.jetbrains.jewel.ui.painter.hints.Stroke
|
||||
import org.jetbrains.jewel.ui.theme.colorPalette
|
||||
|
||||
@Composable
|
||||
internal fun ComponentShowcaseTab() {
|
||||
@@ -68,7 +76,8 @@ internal fun ComponentShowcaseTab() {
|
||||
val scrollState = rememberScrollState()
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.trackComponentActivation(LocalComponent.current)
|
||||
Modifier
|
||||
.trackComponentActivation(LocalComponent.current)
|
||||
.fillMaxSize()
|
||||
.background(bgColor)
|
||||
.verticalScroll(scrollState)
|
||||
@@ -177,27 +186,7 @@ private fun RowScope.ColumnOne() {
|
||||
}
|
||||
}
|
||||
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
Icon(
|
||||
"actions/close.svg",
|
||||
iconClass = AllIcons::class.java,
|
||||
modifier = Modifier.border(1.dp, Color.Magenta),
|
||||
contentDescription = "An icon",
|
||||
)
|
||||
Icon(
|
||||
"icons/github.svg",
|
||||
iconClass = JewelIcons::class.java,
|
||||
modifier = Modifier.border(1.dp, Color.Magenta),
|
||||
contentDescription = "An icon",
|
||||
)
|
||||
|
||||
IconButton(onClick = { }) {
|
||||
Icon("actions/close.svg", contentDescription = "An icon", AllIcons::class.java)
|
||||
}
|
||||
IconButton(onClick = { }) {
|
||||
Icon("actions/addList.svg", contentDescription = "An icon", AllIcons::class.java)
|
||||
}
|
||||
}
|
||||
IconsShowcase()
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
@@ -235,6 +224,74 @@ private fun RowScope.ColumnOne() {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun IconsShowcase() {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
Box(Modifier.size(24.dp), contentAlignment = Alignment.Center) {
|
||||
PlatformIcon(AllIconsKeys.Nodes.ConfigFolder, "taskGroup")
|
||||
}
|
||||
|
||||
Box(Modifier.size(24.dp), contentAlignment = Alignment.Center) {
|
||||
PlatformIcon(AllIconsKeys.Nodes.ConfigFolder, "taskGroup", hint = Badge(Color.Red, DotBadgeShape.Default))
|
||||
}
|
||||
|
||||
Box(
|
||||
Modifier.size(24.dp).background(JewelTheme.colorPalette.blue(4), shape = RoundedCornerShape(4.dp)),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
PlatformIcon(AllIconsKeys.Nodes.ConfigFolder, "taskGroup", hint = Stroke(Color.White))
|
||||
}
|
||||
|
||||
Box(
|
||||
Modifier.size(24.dp).background(JewelTheme.colorPalette.blue(4), shape = RoundedCornerShape(4.dp)),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
PlatformIcon(
|
||||
AllIconsKeys.Nodes.ConfigFolder,
|
||||
"taskGroup",
|
||||
hints = arrayOf(Stroke(Color.White), Badge(Color.Red, DotBadgeShape.Default)),
|
||||
)
|
||||
}
|
||||
|
||||
Box(Modifier.size(24.dp), contentAlignment = Alignment.Center) {
|
||||
PlatformIcon(AllIconsKeys.Nodes.ConfigFolder, "taskGroup", hint = Size(20))
|
||||
}
|
||||
|
||||
Box(Modifier.size(24.dp), contentAlignment = Alignment.Center) {
|
||||
PlatformIcon(
|
||||
AllIconsKeys.Actions.Close,
|
||||
"An icon",
|
||||
modifier = Modifier.border(1.dp, Color.Magenta),
|
||||
hint = Size(20),
|
||||
)
|
||||
}
|
||||
|
||||
Box(Modifier.size(24.dp), contentAlignment = Alignment.Center) {
|
||||
PlatformIcon(AllIconsKeys.Nodes.ConfigFolder, "taskGroup", hint = Size(20))
|
||||
}
|
||||
|
||||
Box(Modifier.size(24.dp), contentAlignment = Alignment.Center) {
|
||||
Icon(
|
||||
IdeSampleIconKeys.gitHub,
|
||||
iconClass = IdeSampleIconKeys::class.java,
|
||||
modifier = Modifier.border(1.dp, Color.Magenta),
|
||||
contentDescription = "An owned icon",
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = { }, Modifier.size(24.dp)) {
|
||||
PlatformIcon(AllIconsKeys.Actions.Close, "Close")
|
||||
}
|
||||
|
||||
IconButton(onClick = { }, Modifier.size(24.dp)) {
|
||||
PlatformIcon(AllIconsKeys.Actions.AddList, "Close")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RowScope.ColumnTwo() {
|
||||
Column(
|
||||
@@ -308,14 +365,18 @@ private fun MarkdownExample() {
|
||||
|fun hello() = "World"
|
||||
|```
|
||||
""".trimMargin(),
|
||||
Modifier.fillMaxWidth()
|
||||
.background(JBUI.CurrentTheme.Banner.INFO_BACKGROUND.toComposeColor(), RoundedCornerShape(8.dp))
|
||||
.border(
|
||||
1.dp,
|
||||
JBUI.CurrentTheme.Banner.INFO_BORDER_COLOR.toComposeColor(),
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
JBUI.CurrentTheme.Banner.INFO_BACKGROUND
|
||||
.toComposeColor(),
|
||||
RoundedCornerShape(8.dp),
|
||||
)
|
||||
.padding(8.dp),
|
||||
).border(
|
||||
1.dp,
|
||||
JBUI.CurrentTheme.Banner.INFO_BORDER_COLOR
|
||||
.toComposeColor(),
|
||||
RoundedCornerShape(8.dp),
|
||||
).padding(8.dp),
|
||||
enabled = enabled,
|
||||
onUrlClick = { url -> BrowserUtil.open(url) },
|
||||
)
|
||||
|
||||
@@ -18,6 +18,7 @@ dependencies {
|
||||
implementation(compose.desktop.currentOs) {
|
||||
exclude(group = "org.jetbrains.compose.material")
|
||||
}
|
||||
implementation(libs.intellijPlatform.icons)
|
||||
}
|
||||
|
||||
compose.desktop {
|
||||
@@ -48,10 +49,11 @@ tasks {
|
||||
// afterEvaluate is needed because the Compose Gradle Plugin
|
||||
// register the task in the afterEvaluate block
|
||||
afterEvaluate {
|
||||
javaLauncher = project.javaToolchains.launcherFor {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
vendor = JvmVendorSpec.JETBRAINS
|
||||
}
|
||||
javaLauncher =
|
||||
project.javaToolchains.launcherFor {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
vendor = JvmVendorSpec.JETBRAINS
|
||||
}
|
||||
setExecutable(javaLauncher.map { it.executablePath.asFile.absolutePath }.get())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
package org.jetbrains.jewel.samples.standalone
|
||||
|
||||
object StandaloneSampleIcons
|
||||
import org.jetbrains.jewel.ui.icon.IntelliJIconKey
|
||||
|
||||
object StandaloneSampleIcons {
|
||||
val gitHub = IntelliJIconKey("icons/github.svg", "icons/github.svg")
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ import org.jetbrains.jewel.foundation.theme.JewelTheme
|
||||
import org.jetbrains.jewel.samples.standalone.StandaloneSampleIcons
|
||||
import org.jetbrains.jewel.samples.standalone.viewmodel.View
|
||||
import org.jetbrains.jewel.ui.component.Icon
|
||||
import org.jetbrains.jewel.ui.component.PlatformIcon
|
||||
import org.jetbrains.jewel.ui.component.Text
|
||||
import org.jetbrains.jewel.ui.icons.AllIconsKeys
|
||||
import org.jetbrains.jewel.ui.painter.badge.DotBadgeShape
|
||||
import org.jetbrains.jewel.ui.painter.hints.Badge
|
||||
import org.jetbrains.jewel.ui.painter.hints.Size
|
||||
@@ -56,35 +58,30 @@ internal fun Icons() {
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
val iconProvider =
|
||||
rememberResourcePainterProvider("icons/taskGroup.svg", StandaloneSampleIcons::class.java)
|
||||
|
||||
val normal by iconProvider.getPainter()
|
||||
val stroked by iconProvider.getPainter(Stroke(Color.White))
|
||||
val badged by iconProvider.getPainter(Badge(Color.Red, DotBadgeShape.Default))
|
||||
val strokedAndBadged by iconProvider.getPainter(Badge(Color.Red, DotBadgeShape.Default), Stroke(Color.White))
|
||||
val resized by iconProvider.getPainter(Size(20))
|
||||
|
||||
Box(Modifier.size(24.dp), contentAlignment = Alignment.Center) {
|
||||
Icon(normal, "taskGroup")
|
||||
PlatformIcon(AllIconsKeys.Nodes.ConfigFolder, "taskGroup")
|
||||
}
|
||||
Box(Modifier.size(24.dp), contentAlignment = Alignment.Center) {
|
||||
Icon(badged, "taskGroup")
|
||||
PlatformIcon(AllIconsKeys.Nodes.ConfigFolder, "taskGroup", hint = Badge(Color.Red, DotBadgeShape.Default))
|
||||
}
|
||||
Box(
|
||||
Modifier.size(24.dp).background(JewelTheme.colorPalette.blue(4), shape = RoundedCornerShape(4.dp)),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(stroked, "taskGroup")
|
||||
PlatformIcon(AllIconsKeys.Nodes.ConfigFolder, "taskGroup", hint = Stroke(Color.White))
|
||||
}
|
||||
Box(
|
||||
Modifier.size(24.dp).background(JewelTheme.colorPalette.blue(4), shape = RoundedCornerShape(4.dp)),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(strokedAndBadged, "taskGroup")
|
||||
PlatformIcon(
|
||||
AllIconsKeys.Nodes.ConfigFolder,
|
||||
"taskGroup",
|
||||
hints = arrayOf(Stroke(Color.White), Badge(Color.Red, DotBadgeShape.Default)),
|
||||
)
|
||||
}
|
||||
Box(Modifier.size(24.dp), contentAlignment = Alignment.Center) {
|
||||
Icon(resized, "taskGroup")
|
||||
PlatformIcon(AllIconsKeys.Nodes.ConfigFolder, "taskGroup", hint = Size(20))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ internal fun MarkdownPreview(
|
||||
val lazyListState = rememberLazyListState()
|
||||
LazyMarkdown(
|
||||
markdownBlocks = markdownBlocks,
|
||||
modifier = modifier.background(background),
|
||||
modifier = Modifier.background(background),
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
state = lazyListState,
|
||||
selectable = true,
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1 3.86667C1 2.83574 1.7835 2 2.75 2H6.03823C6.29871 2 6.5489 2.10163 6.73559 2.28327L8.5 4L13 4C14.1046 4 15 4.89543 15 6V7.38947C14.1372 6.82692 13.1068 6.5 12 6.5C8.96243 6.5 6.5 8.96243 6.5 12C6.5 12.7056 6.63286 13.3801 6.87494 14H2.75C1.7835 14 1 13.1643 1 12.1333V3.86667Z" fill="#EBECF0"/>
|
||||
<path d="M6.03823 3L8.09379 5H13C13.5523 5 14 5.44772 14 6V6.87494C14.3525 7.01259 14.6872 7.18555 15 7.38947V6C15 4.89543 14.1046 4 13 4L8.5 4L6.73559 2.28327C6.5489 2.10163 6.29871 2 6.03823 2H2.75C1.7835 2 1 2.83574 1 3.86667V12.1333C1 13.1643 1.7835 14 2.75 14H6.87494C6.75003 13.6801 6.65419 13.3457 6.59069 13H2.75C2.3956 13 2 12.6738 2 12.1333V3.86667C2 3.32624 2.3956 3 2.75 3H6.03823Z" fill="#6C707E"/>
|
||||
<path d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z" fill="#3574F0"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1216 15.6756C13.0484 15.8707 12.8619 16 12.6534 16H11.3464C11.138 16 10.9515 15.8707 10.8783 15.6756L10.6288 15.0102C10.5449 14.7865 10.3143 14.6534 10.0786 14.6926L9.37765 14.8092C9.17206 14.8434 8.96681 14.7464 8.8626 14.5659L8.2091 13.4341C8.10489 13.2536 8.12357 13.0273 8.25599 12.8664L8.70742 12.3177C8.85925 12.1331 8.85925 11.8669 8.70742 11.6824L8.25597 11.1336C8.12355 10.9727 8.10487 10.7464 8.20908 10.5659L8.86258 9.43405C8.96679 9.25355 9.17204 9.15663 9.37763 9.19083L10.0786 9.30742C10.3143 9.34664 10.5449 9.21353 10.6288 8.98976L10.8783 8.32444C10.9515 8.12929 11.138 8 11.3464 8H12.6534C12.8619 8 13.0484 8.12929 13.1216 8.32444L13.3711 8.98976C13.455 9.21353 13.6856 9.34664 13.9213 9.30742L14.6222 9.19083C14.8278 9.15663 15.0331 9.25355 15.1373 9.43405L15.7908 10.5659C15.895 10.7464 15.8763 10.9727 15.7439 11.1336L15.2925 11.6824C15.1406 11.8669 15.1406 12.1331 15.2925 12.3177L15.7439 12.8664C15.8763 13.0273 15.895 13.2536 15.7908 13.4341L15.1373 14.5659C15.0331 14.7464 14.8278 14.8434 14.6222 14.8092L13.9213 14.6926C13.6856 14.6534 13.455 14.7865 13.3711 15.0102L13.1216 15.6756ZM11.6929 15H12.3069L12.4348 14.6591C12.6865 13.9878 13.3781 13.5885 14.0854 13.7061L14.4445 13.7659L14.7515 13.2341L14.5202 12.953C14.0647 12.3993 14.0647 11.6007 14.5202 11.047L14.7515 10.7659L14.4445 10.2341L14.0854 10.2939C13.3781 10.4115 12.6865 10.0122 12.4348 9.34088L12.3069 9H11.6929L11.5651 9.34088C11.3134 10.0122 10.6217 10.4115 9.91449 10.2939L9.55536 10.2341L9.24836 10.7659L9.47966 11.047C9.93517 11.6007 9.93517 12.3993 9.47966 12.953L9.24838 13.2341L9.55538 13.7659L9.91449 13.7061C10.6217 13.5885 11.3134 13.9878 11.5651 14.6591L11.6929 15Z" fill="#3574F0"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.3069 15H11.6929L11.5651 14.6591C11.3134 13.9878 10.6217 13.5885 9.91449 13.7061L9.55537 13.7659L9.24837 13.2341L9.47966 12.953C9.93517 12.3993 9.93517 11.6007 9.47966 11.047L9.24835 10.7659L9.55535 10.2341L9.91449 10.2939C10.6217 10.4115 11.3134 10.0122 11.5651 9.34088L11.6929 9H12.3069L12.4348 9.34088C12.6865 10.0122 13.3781 10.4115 14.0854 10.2939L14.4445 10.2341L14.7515 10.7659L14.5202 11.047C14.0647 11.6007 14.0647 12.3993 14.5202 12.953L14.7515 13.2341L14.4445 13.7659L14.0854 13.7061C13.3781 13.5885 12.6865 13.9878 12.4348 14.6591L12.3069 15ZM13 12C13 12.5523 12.5523 13 12 13C11.4477 13 11 12.5523 11 12C11 11.4477 11.4477 11 12 11C12.5523 11 13 11.4477 13 12Z" fill="#E7EFFD"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.5 KiB |
@@ -1,8 +0,0 @@
|
||||
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1 3.86667C1 2.83574 1.7835 2 2.75 2H6.03823C6.29871 2 6.5489 2.10163 6.73559 2.28327L8.5 4L13 4C14.1046 4 15 4.89543 15 6V7.38947C14.1372 6.82692 13.1068 6.5 12 6.5C8.96243 6.5 6.5 8.96243 6.5 12C6.5 12.7056 6.63286 13.3801 6.87494 14H2.75C1.7835 14 1 13.1643 1 12.1333V3.86667Z" fill="#43454A"/>
|
||||
<path d="M6.03823 3L8.09379 5H13C13.5523 5 14 5.44772 14 6V6.87494C14.3525 7.01259 14.6872 7.18555 15 7.38947V6C15 4.89543 14.1046 4 13 4L8.5 4L6.73559 2.28327C6.5489 2.10163 6.29871 2 6.03823 2H2.75C1.7835 2 1 2.83574 1 3.86667V12.1333C1 13.1643 1.7835 14 2.75 14H6.87494C6.75003 13.6801 6.65419 13.3457 6.59069 13H2.75C2.3956 13 2 12.6738 2 12.1333V3.86667C2 3.32624 2.3956 3 2.75 3H6.03823Z" fill="#CED0D6"/>
|
||||
<path d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z" fill="#548AF7"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1216 15.6756C13.0484 15.8707 12.8619 16 12.6534 16H11.3464C11.138 16 10.9515 15.8707 10.8783 15.6756L10.6288 15.0102C10.5449 14.7865 10.3143 14.6534 10.0786 14.6926L9.37765 14.8092C9.17206 14.8434 8.96681 14.7464 8.8626 14.5659L8.2091 13.4341C8.10489 13.2536 8.12357 13.0273 8.25599 12.8664L8.70742 12.3177C8.85925 12.1331 8.85925 11.8669 8.70742 11.6824L8.25597 11.1336C8.12355 10.9727 8.10487 10.7464 8.20908 10.5659L8.86258 9.43405C8.96679 9.25355 9.17204 9.15663 9.37763 9.19083L10.0786 9.30742C10.3143 9.34664 10.5449 9.21353 10.6288 8.98976L10.8783 8.32444C10.9515 8.12929 11.138 8 11.3464 8H12.6534C12.8619 8 13.0484 8.12929 13.1216 8.32444L13.3711 8.98976C13.455 9.21353 13.6856 9.34664 13.9213 9.30742L14.6222 9.19083C14.8278 9.15663 15.0331 9.25355 15.1373 9.43405L15.7908 10.5659C15.895 10.7464 15.8763 10.9727 15.7439 11.1336L15.2925 11.6824C15.1406 11.8669 15.1406 12.1331 15.2925 12.3177L15.7439 12.8664C15.8763 13.0273 15.895 13.2536 15.7908 13.4341L15.1373 14.5659C15.0331 14.7464 14.8278 14.8434 14.6222 14.8092L13.9213 14.6926C13.6856 14.6534 13.455 14.7865 13.3711 15.0102L13.1216 15.6756ZM11.6929 15H12.3069L12.4348 14.6591C12.6865 13.9878 13.3781 13.5885 14.0854 13.7061L14.4445 13.7659L14.7515 13.2341L14.5202 12.953C14.0647 12.3993 14.0647 11.6007 14.5202 11.047L14.7515 10.7659L14.4445 10.2341L14.0854 10.2939C13.3781 10.4115 12.6865 10.0122 12.4348 9.34088L12.3069 9H11.6929L11.5651 9.34088C11.3134 10.0122 10.6217 10.4115 9.91449 10.2939L9.55536 10.2341L9.24836 10.7659L9.47966 11.047C9.93517 11.6007 9.93517 12.3993 9.47966 12.953L9.24838 13.2341L9.55538 13.7659L9.91449 13.7061C10.6217 13.5885 11.3134 13.9878 11.5651 14.6591L11.6929 15Z" fill="#548AF7"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.3069 15H11.6929L11.5651 14.6591C11.3134 13.9878 10.6217 13.5885 9.91449 13.7061L9.55537 13.7659L9.24837 13.2341L9.47966 12.953C9.93517 12.3993 9.93517 11.6007 9.47966 11.047L9.24835 10.7659L9.55535 10.2341L9.91449 10.2939C10.6217 10.4115 11.3134 10.0122 11.5651 9.34088L11.6929 9H12.3069L12.4348 9.34088C12.6865 10.0122 13.3781 10.4115 14.0854 10.2939L14.4445 10.2341L14.7515 10.7659L14.5202 11.047C14.0647 11.6007 14.0647 12.3993 14.5202 12.953L14.7515 13.2341L14.4445 13.7659L14.0854 13.7061C13.3781 13.5885 12.6865 13.9878 12.4348 14.6591L12.3069 15ZM13 12C13 12.5523 12.5523 13 12 13C11.4477 13 11 12.5523 11 12C11 11.4477 11.4477 11 12 11C12.5523 11 13 11.4477 13 12Z" fill="#25324D"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.5 KiB |
@@ -18,6 +18,9 @@ dependencyResolutionManagement {
|
||||
repositories {
|
||||
google()
|
||||
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
|
||||
maven("https://www.jetbrains.com/intellij-repository/releases")
|
||||
maven("https://www.jetbrains.com/intellij-repository/snapshots")
|
||||
maven("https://cache-redirector.jetbrains.com/intellij-dependencies")
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,12 @@
|
||||
import org.jetbrains.compose.ComposeBuildConfig
|
||||
import org.jmailen.gradle.kotlinter.tasks.FormatTask
|
||||
import org.jmailen.gradle.kotlinter.tasks.LintTask
|
||||
|
||||
plugins {
|
||||
jewel
|
||||
`jewel-publish`
|
||||
`jewel-check-public-api`
|
||||
`icon-keys-generator`
|
||||
alias(libs.plugins.composeDesktop)
|
||||
alias(libs.plugins.kotlinx.serialization)
|
||||
}
|
||||
@@ -13,9 +16,25 @@ private val composeVersion
|
||||
|
||||
dependencies {
|
||||
api(projects.foundation)
|
||||
|
||||
iconGeneration(libs.intellijPlatform.util.ui)
|
||||
iconGeneration(libs.intellijPlatform.icons)
|
||||
testImplementation(compose.desktop.uiTestJUnit4)
|
||||
testImplementation(compose.desktop.currentOs) {
|
||||
exclude(group = "org.jetbrains.compose.material")
|
||||
}
|
||||
}
|
||||
|
||||
intelliJIconKeysGenerator {
|
||||
register("AllIcons") {
|
||||
sourceClassName = "com.intellij.icons.AllIcons"
|
||||
generatedClassName = "org.jetbrains.jewel.ui.icons.AllIconsKeys"
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<LintTask> {
|
||||
include("src/**") // Excluding build/ doesn't work for some reason
|
||||
}
|
||||
|
||||
tasks.withType<FormatTask> {
|
||||
include("src/**") // Excluding build/ doesn't work for some reason
|
||||
}
|
||||
|
||||
@@ -31,6 +31,10 @@ import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.role
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.jetbrains.jewel.foundation.theme.JewelTheme
|
||||
import org.jetbrains.jewel.ui.icon.IconKey
|
||||
import org.jetbrains.jewel.ui.icon.newUiChecker
|
||||
import org.jetbrains.jewel.ui.painter.PainterHint
|
||||
import org.jetbrains.jewel.ui.painter.rememberResourcePainterProvider
|
||||
import org.jetbrains.jewel.ui.util.thenIf
|
||||
import org.xml.sax.InputSource
|
||||
@@ -43,9 +47,30 @@ public fun Icon(
|
||||
iconClass: Class<*>,
|
||||
colorFilter: ColorFilter?,
|
||||
modifier: Modifier = Modifier,
|
||||
vararg hints: PainterHint,
|
||||
) {
|
||||
val painterProvider = rememberResourcePainterProvider(resource, iconClass)
|
||||
val painter by painterProvider.getPainter()
|
||||
val painter by painterProvider.getPainter(*hints)
|
||||
|
||||
Icon(
|
||||
painter = painter,
|
||||
contentDescription = contentDescription,
|
||||
modifier = modifier,
|
||||
colorFilter = colorFilter,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
public fun Icon(
|
||||
resource: String,
|
||||
contentDescription: String?,
|
||||
iconClass: Class<*>,
|
||||
colorFilter: ColorFilter?,
|
||||
modifier: Modifier = Modifier,
|
||||
hint: PainterHint,
|
||||
) {
|
||||
val painterProvider = rememberResourcePainterProvider(resource, iconClass)
|
||||
val painter by painterProvider.getPainter(hint)
|
||||
|
||||
Icon(
|
||||
painter = painter,
|
||||
@@ -62,9 +87,10 @@ public fun Icon(
|
||||
iconClass: Class<*>,
|
||||
modifier: Modifier = Modifier,
|
||||
tint: Color = Color.Unspecified,
|
||||
vararg hints: PainterHint,
|
||||
) {
|
||||
val painterProvider = rememberResourcePainterProvider(resource, iconClass)
|
||||
val painter by painterProvider.getPainter()
|
||||
val painter by painterProvider.getPainter(*hints)
|
||||
|
||||
Icon(
|
||||
painter = painter,
|
||||
@@ -74,6 +100,54 @@ public fun Icon(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
public fun Icon(
|
||||
resource: String,
|
||||
contentDescription: String?,
|
||||
iconClass: Class<*>,
|
||||
modifier: Modifier = Modifier,
|
||||
tint: Color = Color.Unspecified,
|
||||
hint: PainterHint,
|
||||
) {
|
||||
val painterProvider = rememberResourcePainterProvider(resource, iconClass)
|
||||
val painter by painterProvider.getPainter(hint)
|
||||
|
||||
Icon(
|
||||
painter = painter,
|
||||
contentDescription = contentDescription,
|
||||
modifier = modifier,
|
||||
tint = tint,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
public fun Icon(
|
||||
key: IconKey,
|
||||
contentDescription: String?,
|
||||
iconClass: Class<*>,
|
||||
modifier: Modifier = Modifier,
|
||||
tint: Color = Color.Unspecified,
|
||||
vararg hints: PainterHint,
|
||||
) {
|
||||
val isNewUi = JewelTheme.newUiChecker.isNewUi()
|
||||
val path = remember(key, isNewUi) { key.path(isNewUi) }
|
||||
Icon(path, contentDescription, iconClass, modifier, tint, *hints)
|
||||
}
|
||||
|
||||
@Composable
|
||||
public fun Icon(
|
||||
key: IconKey,
|
||||
contentDescription: String?,
|
||||
iconClass: Class<*>,
|
||||
modifier: Modifier = Modifier,
|
||||
tint: Color = Color.Unspecified,
|
||||
hint: PainterHint,
|
||||
) {
|
||||
val isNewUi = JewelTheme.newUiChecker.isNewUi()
|
||||
val path = remember(key, isNewUi) { key.path(isNewUi) }
|
||||
Icon(path, contentDescription, iconClass, modifier, tint, hint)
|
||||
}
|
||||
|
||||
/**
|
||||
* Icon component that draws [imageVector] using [tint], defaulting to
|
||||
* [Color.Unspecified].
|
||||
@@ -183,14 +257,14 @@ public fun Icon(
|
||||
Modifier
|
||||
}
|
||||
Box(
|
||||
modifier.toolingGraphicsLayer()
|
||||
modifier
|
||||
.toolingGraphicsLayer()
|
||||
.defaultSizeFor(painter)
|
||||
.paint(
|
||||
painter,
|
||||
colorFilter = colorFilter,
|
||||
contentScale = ContentScale.Fit,
|
||||
)
|
||||
.then(semantics),
|
||||
).then(semantics),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.jetbrains.jewel.ui.component
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import org.jetbrains.jewel.ui.icon.IntelliJIconKey
|
||||
import org.jetbrains.jewel.ui.icons.AllIconsKeys
|
||||
import org.jetbrains.jewel.ui.painter.PainterHint
|
||||
|
||||
@Composable
|
||||
public fun PlatformIcon(
|
||||
key: IntelliJIconKey,
|
||||
contentDescription: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
tint: Color = Color.Unspecified,
|
||||
hint: PainterHint,
|
||||
) {
|
||||
PlatformIcon(key, contentDescription, modifier, tint, *arrayOf(hint))
|
||||
}
|
||||
|
||||
@Composable
|
||||
public fun PlatformIcon(
|
||||
key: IntelliJIconKey,
|
||||
contentDescription: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
tint: Color = Color.Unspecified,
|
||||
vararg hints: PainterHint,
|
||||
) {
|
||||
Icon(key, contentDescription, AllIconsKeys::class.java, modifier, tint, *hints)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.jetbrains.jewel.ui.icon
|
||||
|
||||
import org.jetbrains.jewel.foundation.GenerateDataFunctions
|
||||
|
||||
public interface IconKey {
|
||||
public fun path(isNewUi: Boolean): String
|
||||
}
|
||||
|
||||
@GenerateDataFunctions
|
||||
public class PathIconKey(
|
||||
private val path: String,
|
||||
) : IconKey {
|
||||
override fun path(isNewUi: Boolean): String = path
|
||||
}
|
||||
|
||||
@GenerateDataFunctions
|
||||
public class IntelliJIconKey(
|
||||
public val oldUiPath: String,
|
||||
public val newUiPath: String,
|
||||
) : IconKey {
|
||||
override fun path(isNewUi: Boolean): String = if (isNewUi) newUiPath else oldUiPath
|
||||
|
||||
public companion object
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.jetbrains.jewel.ui.icon
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ProvidableCompositionLocal
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import org.jetbrains.jewel.foundation.theme.JewelTheme
|
||||
|
||||
public fun interface NewUiChecker {
|
||||
public fun isNewUi(): Boolean
|
||||
}
|
||||
|
||||
public val LocalNewUiChecker: ProvidableCompositionLocal<NewUiChecker> =
|
||||
staticCompositionLocalOf { error("No NewUiChecker provided") }
|
||||
|
||||
public val JewelTheme.Companion.newUiChecker: NewUiChecker
|
||||
@Composable get() = LocalNewUiChecker.current
|
||||
@@ -9,6 +9,7 @@ import org.jetbrains.jewel.ui.painter.PainterHint
|
||||
import org.jetbrains.jewel.ui.painter.PainterProviderScope
|
||||
import org.jetbrains.jewel.ui.painter.PainterWrapperHint
|
||||
import org.jetbrains.jewel.ui.painter.badge.BadgeShape
|
||||
import org.jetbrains.jewel.ui.painter.badge.DotBadgeShape
|
||||
|
||||
@GenerateDataFunctions
|
||||
private class BadgeImpl(
|
||||
@@ -22,5 +23,5 @@ private class BadgeImpl(
|
||||
@Suppress("FunctionName")
|
||||
public fun Badge(
|
||||
color: Color,
|
||||
shape: BadgeShape,
|
||||
shape: BadgeShape = DotBadgeShape.Default,
|
||||
): PainterHint = if (color.isSpecified) BadgeImpl(color, shape) else PainterHint.None
|
||||
|
||||
Reference in New Issue
Block a user