Add JewelLogger (#443)

* add kotlin-logging to stand alone app

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* replace logger in ChipsAndTree

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* iterate on the logger template

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* replace logged in Views.kt

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* idea.log path has changed 🤷

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* add logger to IDE sample

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* move LoggerRt into Jewel

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* fix Compose lint warnings in ComponentShowcaseTab

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* rename logger to JewelLogger

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* migrate standalone to JewelLogger

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* replace logger in JewelDemoAction

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* replace logger in StandalonePainterHintsProvider

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* replace logger in ResourcePainterProvider

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* add missing empty line at EOF in libs.versions.toml

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* add debug() for Java logger

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* add and test .trace() to Java logger

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* tune logger setup for IDE plugin scenario

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* fix logger category for Views.kt

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* fix logger category for ChipsAndTree

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* remove unused logback configuration

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* replace logger in StandalonePainterHintsProvider

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* replace logger in ResourcePainterProvider

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* address PR comments

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* iterate on logger usage in ResourcePainterProvider

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* refactor JewelLogger instance factory creation

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* run apiDump

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* move log to info in ChipsAndTree

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

* address PR review feedback

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>

---------

Signed-off-by: Ivan Morgillo <imorgillo@gmail.com>
GitOrigin-RevId: d6f89b000bb75b0eb4461867671f9d02d74daa20
This commit is contained in:
Ivan Morgillo
2024-07-11 16:03:49 +02:00
committed by intellij-monorepo-bot
parent f1a6ac3478
commit 916e2796b0
11 changed files with 485 additions and 84 deletions

View File

@@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="IDE sample" type="GradleRunConfiguration" factoryName="Gradle" show_console_on_std_err="true" show_console_on_std_out="true">
<log_file alias="idea.log" path="$PROJECT_DIR$/samples/ide-plugin/build/idea-sandbox/system/log/idea.log" />
<log_file alias="IDE Logs" path="$PROJECT_DIR$/samples/ide-plugin/build/idea-sandbox/IC-2024.2/log/idea.log" show_all="true" />
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$/samples/ide-plugin" />

View File

@@ -880,3 +880,29 @@ public final class org/jetbrains/jewel/foundation/util/DebugKt {
public static final fun getInDebugMode ()Z
}
public abstract class org/jetbrains/jewel/foundation/util/JewelLogger {
public static final field $stable I
public static final field Companion Lorg/jetbrains/jewel/foundation/util/JewelLogger$Companion;
public fun <init> ()V
public final fun debug (Ljava/lang/String;)V
public abstract fun debug (Ljava/lang/String;Ljava/lang/Throwable;)V
public final fun debug (Ljava/lang/Throwable;)V
public final fun error (Ljava/lang/String;)V
public abstract fun error (Ljava/lang/String;Ljava/lang/Throwable;)V
public final fun error (Ljava/lang/Throwable;)V
public final fun info (Ljava/lang/String;)V
public abstract fun info (Ljava/lang/String;Ljava/lang/Throwable;)V
public final fun info (Ljava/lang/Throwable;)V
public final fun trace (Ljava/lang/String;)V
public abstract fun trace (Ljava/lang/String;Ljava/lang/Throwable;)V
public final fun trace (Ljava/lang/Throwable;)V
public final fun warn (Ljava/lang/String;)V
public abstract fun warn (Ljava/lang/String;Ljava/lang/Throwable;)V
public final fun warn (Ljava/lang/Throwable;)V
}
public final class org/jetbrains/jewel/foundation/util/JewelLogger$Companion {
public final fun getInstance (Ljava/lang/Class;)Lorg/jetbrains/jewel/foundation/util/JewelLogger;
public final fun getInstance (Ljava/lang/String;)Lorg/jetbrains/jewel/foundation/util/JewelLogger;
}

View File

@@ -0,0 +1,395 @@
package org.jetbrains.jewel.foundation.util
import org.jetbrains.annotations.ApiStatus
import java.lang.reflect.Method
import java.util.logging.ConsoleHandler
import java.util.logging.Level
import java.util.logging.Logger
public inline fun <reified T : Any> T.myLogger(): JewelLogger = JewelLogger.getInstance(T::class.java)
/**
* A wrapper which uses either IDE logging subsystem (if available) or java.util.logging.
*/
@ApiStatus.NonExtendable
@Suppress("OptionalUnit")
public abstract class JewelLogger {
private interface Factory {
fun getInstance(category: String?): JewelLogger
}
public fun trace(message: String?): Unit = trace(message, null)
public fun trace(t: Throwable): Unit = trace(t.message, t)
public fun debug(message: String?): Unit = debug(message, null)
public fun debug(t: Throwable): Unit = debug(t.message, t)
public fun info(message: String?): Unit = info(message, null)
public fun info(t: Throwable): Unit = info(t.message, t)
public fun warn(message: String?): Unit = warn(message, null)
public fun warn(t: Throwable): Unit = warn(t.message, t)
public fun error(message: String?): Unit = error(message, null)
public fun error(t: Throwable): Unit = error(t.message, t)
public abstract fun trace(
message: String?,
t: Throwable?,
)
public abstract fun debug(
message: String?,
t: Throwable?,
)
public abstract fun info(
message: String?,
t: Throwable?,
)
public abstract fun warn(
message: String?,
t: Throwable?,
)
public abstract fun error(
message: String?,
t: Throwable?,
)
private class JavaFactory : Factory {
override fun getInstance(category: String?): JewelLogger {
val logger by lazy {
val l = Logger.getLogger(category)
// Remove existing default handlers to avoid duplicate messages in console
for (handler in l.handlers) {
l.removeHandler(handler)
}
// Create a new handler with a higher logging level
val handler = ConsoleHandler()
handler.level = Level.FINER
l.addHandler(handler)
// Tune the logger for level and duplicated messages
l.level = Level.FINER
l.useParentHandlers = false
l
}
return object : JewelLogger() {
override fun trace(
message: String?,
t: Throwable?,
) {
logger.log(Level.FINER, message, t)
}
override fun debug(
message: String?,
t: Throwable?,
) {
logger.log(Level.FINE, message, t)
}
override fun info(
message: String?,
t: Throwable?,
) {
logger.log(Level.INFO, message, t)
}
override fun warn(
message: String?,
t: Throwable?,
) {
logger.log(Level.WARNING, message, t)
}
override fun error(
message: String?,
t: Throwable?,
) {
logger.log(Level.SEVERE, message, t)
}
}
}
}
private abstract class ReflectionBasedFactory : Factory {
@Throws(RuntimeException::class)
@Suppress("TooGenericExceptionCaught", "TooGenericExceptionThrown")
override fun getInstance(category: String?): JewelLogger {
try {
val logger = getLogger(category)
return object : JewelLogger() {
override fun trace(
message: String?,
t: Throwable?,
) {
try {
this@ReflectionBasedFactory.trace(message, t, logger)
} catch (ignored: Exception) {
}
}
override fun debug(
message: String?,
t: Throwable?,
) {
try {
this@ReflectionBasedFactory.debug(message, t, logger)
} catch (ignored: Exception) {
}
}
override fun info(
message: String?,
t: Throwable?,
) {
try {
this@ReflectionBasedFactory.info(message, t, logger)
} catch (ignored: Exception) {
}
}
override fun warn(
message: String?,
t: Throwable?,
) {
try {
this@ReflectionBasedFactory.warn(message, t, logger)
} catch (ignored: Exception) {
}
}
override fun error(
message: String?,
t: Throwable?,
) {
try {
this@ReflectionBasedFactory.error(message, t, logger)
} catch (ignored: Exception) {
}
}
}
} catch (e: Exception) {
throw RuntimeException(e)
}
}
@Throws(Exception::class)
protected abstract fun trace(
message: String?,
t: Throwable?,
logger: Any?,
)
@Throws(Exception::class)
protected abstract fun debug(
message: String?,
t: Throwable?,
logger: Any?,
)
@Throws(Exception::class)
protected abstract fun error(
message: String?,
t: Throwable?,
logger: Any?,
)
@Throws(Exception::class)
protected abstract fun warn(
message: String?,
t: Throwable?,
logger: Any?,
)
@Throws(Exception::class)
protected abstract fun info(
message: String?,
t: Throwable?,
logger: Any?,
)
@Throws(Exception::class)
protected abstract fun getLogger(category: String?): Any
}
private class IdeaFactory : ReflectionBasedFactory() {
private val myGetInstance: Method
private val ideaTrace: Method
private val ideaDebug: Method
private val ideaInfo: Method
private val ideaWarn: Method
private val ideaError: Method
init {
val loggerClass = Class.forName("com.intellij.openapi.diagnostic.Logger")
myGetInstance = loggerClass.getMethod("getInstance", String::class.java)
myGetInstance.isAccessible = true
ideaTrace = loggerClass.getMethod("trace", Throwable::class.java)
ideaTrace.isAccessible = true
ideaDebug = loggerClass.getMethod("debug", String::class.java, Throwable::class.java)
ideaDebug.isAccessible = true
ideaInfo = loggerClass.getMethod("info", String::class.java, Throwable::class.java)
ideaInfo.isAccessible = true
ideaWarn = loggerClass.getMethod("warn", String::class.java, Throwable::class.java)
ideaWarn.isAccessible = true
ideaError = loggerClass.getMethod("error", String::class.java, Throwable::class.java)
ideaError.isAccessible = true
}
@Throws(Exception::class)
override fun trace(
message: String?,
t: Throwable?,
logger: Any?,
) {
ideaTrace.invoke(logger, t)
}
@Throws(Exception::class)
override fun debug(
message: String?,
t: Throwable?,
logger: Any?,
) {
ideaDebug.invoke(logger, message, t)
}
@Throws(Exception::class)
override fun error(
message: String?,
t: Throwable?,
logger: Any?,
) {
ideaError.invoke(logger, message, t)
}
@Throws(Exception::class)
override fun warn(
message: String?,
t: Throwable?,
logger: Any?,
) {
ideaWarn.invoke(logger, message, t)
}
@Throws(Exception::class)
override fun info(
message: String?,
t: Throwable?,
logger: Any?,
) {
ideaInfo.invoke(logger, message, t)
}
@Throws(Exception::class)
override fun getLogger(category: String?): Any = myGetInstance.invoke(null, category)
}
private class Slf4JFactory : ReflectionBasedFactory() {
private val myGetLogger: Method
private val myTrace: Method
private val myDebug: Method
private val myInfo: Method
private val myWarn: Method
private val myError: Method
init {
val loggerFactoryClass = Class.forName("org.slf4j.LoggerFactory")
myGetLogger = loggerFactoryClass.getMethod("getLogger", String::class.java)
myGetLogger.isAccessible = true
val loggerClass = Class.forName("org.slf4j.Logger")
myTrace = loggerClass.getMethod("trace", String::class.java, Throwable::class.java)
myTrace.isAccessible = true
myDebug = loggerClass.getMethod("debug", String::class.java, Throwable::class.java)
myDebug.isAccessible = true
myInfo = loggerClass.getMethod("info", String::class.java, Throwable::class.java)
myInfo.isAccessible = true
myWarn = loggerClass.getMethod("warn", String::class.java, Throwable::class.java)
myWarn.isAccessible = true
myError = loggerClass.getMethod("error", String::class.java, Throwable::class.java)
myError.isAccessible = true
}
@Throws(Exception::class)
override fun trace(
message: String?,
t: Throwable?,
logger: Any?,
) {
myTrace.invoke(logger, message, t)
}
@Throws(Exception::class)
override fun debug(
message: String?,
t: Throwable?,
logger: Any?,
) {
myDebug.invoke(logger, message, t)
}
@Throws(Exception::class)
override fun error(
message: String?,
t: Throwable?,
logger: Any?,
) {
myError.invoke(logger, message, t)
}
@Throws(Exception::class)
override fun warn(
message: String?,
t: Throwable?,
logger: Any?,
) {
myWarn.invoke(logger, message, t)
}
@Throws(Exception::class)
override fun info(
message: String?,
t: Throwable?,
logger: Any?,
) {
myInfo.invoke(logger, message, t)
}
@Throws(Exception::class)
override fun getLogger(category: String?): Any = myGetLogger.invoke(null, category)
}
public companion object {
@get:Synchronized
private val factory: Factory = createFactory()
@Suppress("SwallowedException", "TooGenericExceptionCaught")
private fun createFactory(): Factory =
try {
IdeaFactory()
} catch (t: Throwable) {
try {
Slf4JFactory()
} catch (t2: Throwable) {
JavaFactory()
}
}
public fun getInstance(category: String): JewelLogger = factory.getInstance("#$category")
public fun getInstance(clazz: Class<*>): JewelLogger = getInstance('#'.toString() + clazz.name)
}
}

View File

@@ -1,42 +0,0 @@
package org.jetbrains.jewel.foundation.util
internal enum class LogLevel(val color: String) {
Trace("\u001b[38;5;33m"),
Debug("\u001b[35;1m"),
Info("\u001b[38;5;77m"),
Warn("\u001b[33;1m"),
Error("\u001b[31;1m"),
Off(""),
}
internal interface Logger {
var currentLogLevel: LogLevel
// Resets previous color codes
private fun resetColor() = "\u001b[0m"
public fun log(
level: LogLevel,
msg: String,
)
public fun e(msg: String) {
log(LogLevel.Error, LogLevel.Error.color + msg + resetColor())
}
public fun d(msg: String) {
log(LogLevel.Debug, LogLevel.Debug.color + msg + resetColor())
}
public fun w(msg: String) {
log(LogLevel.Warn, LogLevel.Warn.color + msg + resetColor())
}
public fun i(msg: String) {
log(LogLevel.Info, LogLevel.Info.color + msg + resetColor())
}
public fun t(msg: String) {
log(LogLevel.Trace, LogLevel.Trace.color + msg + resetColor())
}
}

View File

@@ -5,6 +5,7 @@ import androidx.compose.ui.graphics.Color
import org.jetbrains.jewel.foundation.theme.JewelTheme
import org.jetbrains.jewel.foundation.theme.ThemeDefinition
import org.jetbrains.jewel.foundation.util.inDebugMode
import org.jetbrains.jewel.foundation.util.myLogger
import org.jetbrains.jewel.ui.painter.PainterHint
import org.jetbrains.jewel.ui.painter.PalettePainterHintsProvider
import org.jetbrains.jewel.ui.painter.hints.ColorBasedPaletteReplacement
@@ -22,6 +23,8 @@ public class StandalonePainterHintsProvider(
theme.iconData.colorPalette,
theme.colorPalette.rawMap,
) {
private val logger = myLogger()
override val checkBoxByColorPaletteHint: PainterHint
override val checkBoxByKeyPaletteHint: PainterHint
override val treePaletteHint: PainterHint
@@ -83,19 +86,19 @@ public class StandalonePainterHintsProvider(
if (adjustedKey !in supportedCheckboxKeys) {
if (inDebugMode) {
println("${if (isDark) "Dark" else "Light"} theme: color key $key is not supported, will be ignored")
logger.debug("${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")
logger.debug("${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'")
logger.debug("${if (isDark) "Dark" else "Light"} theme: color key $key has invalid value: '$value'")
}
return
}

View File

@@ -19,6 +19,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -124,7 +125,7 @@ private fun RowScope.ColumnOne() {
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
var clicks1 by remember { mutableStateOf(0) }
var clicks1 by remember { mutableIntStateOf(0) }
OutlinedButton({ clicks1++ }) {
Text("Outlined: $clicks1")
}
@@ -132,7 +133,7 @@ private fun RowScope.ColumnOne() {
Text("Outlined")
}
var clicks2 by remember { mutableStateOf(0) }
var clicks2 by remember { mutableIntStateOf(0) }
DefaultButton({ clicks2++ }) {
Text("Default: $clicks2")
}
@@ -168,7 +169,7 @@ private fun RowScope.ColumnOne() {
}
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
var index by remember { mutableStateOf(0) }
var index by remember { mutableIntStateOf(0) }
RadioButtonRow(
selected = index == 0,
onClick = { index = 0 },
@@ -217,7 +218,7 @@ private fun RowScope.ColumnOne() {
}
}
var sliderValue by remember { mutableStateOf(.15f) }
var sliderValue by remember { mutableFloatStateOf(.15f) }
Slider(sliderValue, { sliderValue = it }, steps = 5)
}
}

View File

@@ -7,7 +7,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
@@ -30,7 +30,9 @@ import org.jetbrains.jewel.ui.component.Typography
import kotlin.time.Duration.Companion.seconds
@Service(Service.Level.PROJECT)
private class ProjectScopeProviderService(val scope: CoroutineScope)
private class ProjectScopeProviderService(
val scope: CoroutineScope,
)
internal class JewelDemoAction : DumbAwareAction() {
override fun actionPerformed(event: AnActionEvent) {
@@ -47,7 +49,9 @@ internal class JewelDemoAction : DumbAwareAction() {
}
}
private class FirstPage(private val project: Project) : WizardPage {
private class FirstPage(
private val project: Project,
) : WizardPage {
override val canGoBackwards: StateFlow<Boolean> = MutableStateFlow(true)
private val checkboxChecked = MutableStateFlow(false)
@@ -65,10 +69,7 @@ private class FirstPage(private val project: Project) : WizardPage {
Spacer(Modifier.height(16.dp))
val checked by checkboxChecked.collectAsState()
CheckboxRow("Allow going to next step", checked, {
checkboxChecked.value = it
println("Checkbox value: ${checkboxChecked.value}")
})
CheckboxRow("Allow going to next step", checked, { checkboxChecked.value = it })
}
}
}
@@ -84,7 +85,7 @@ private class SecondPage : WizardPage {
Spacer(Modifier.height(16.dp))
var count by remember { mutableStateOf(0) }
var count by remember { mutableIntStateOf(0) }
LaunchedEffect(Unit) {
launch {
while (true) {

View File

@@ -7,6 +7,7 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.Density
import androidx.compose.ui.window.application
import org.jetbrains.jewel.foundation.theme.JewelTheme
import org.jetbrains.jewel.foundation.util.JewelLogger
import org.jetbrains.jewel.intui.standalone.Inter
import org.jetbrains.jewel.intui.standalone.JetBrainsMono
import org.jetbrains.jewel.intui.standalone.theme.IntUiTheme
@@ -27,6 +28,10 @@ import org.jetbrains.jewel.window.styling.TitleBarStyle
import java.io.InputStream
fun main() {
JewelLogger
.getInstance("StandaloneSample")
.info("Starting Jewel Standalone sample")
val icon = svgResource("icons/jewel-logo.svg")
application {
@@ -75,7 +80,8 @@ private fun svgResource(
resourcePath: String,
loader: ResourceLoader = ResourceLoader.Default,
): Painter =
loader.load(resourcePath)
loader
.load(resourcePath)
.use { stream: InputStream ->
loadSvgPainter(stream, Density(1f))
}

View File

@@ -1,6 +1,7 @@
package org.jetbrains.jewel.samples.standalone.reflection
import androidx.compose.runtime.Composable
import org.jetbrains.jewel.foundation.util.JewelLogger
import org.jetbrains.jewel.samples.standalone.viewmodel.View
import org.jetbrains.jewel.samples.standalone.viewmodel.ViewInfo
import org.jetbrains.jewel.ui.icon.PathIconKey
@@ -16,8 +17,10 @@ internal fun findViews(packageName: String): List<ViewInfo> {
val path = "/" + packageName.replace('.', '/').removePrefix("/")
val uri =
Class.forName("org.jetbrains.jewel.samples.standalone.reflection.ViewsKt")
.getResource(path)?.toURI() ?: return emptyList()
Class
.forName("org.jetbrains.jewel.samples.standalone.reflection.ViewsKt")
.getResource(path)
?.toURI() ?: return emptyList()
val directory =
if (uri.scheme == "jar") {
@@ -35,12 +38,15 @@ internal fun findViews(packageName: String): List<ViewInfo> {
val result = mutableListOf<ViewInfo>()
if (Files.exists(directory)) {
Files.list(directory)
Files
.list(directory)
.filter { f -> Files.isRegularFile(f) && !f.name.contains('$') && f.name.endsWith("Kt.class") }
.forEach { f ->
val fullyQualifiedClassName =
packageName +
f.absolutePathString().removePrefix(directory.absolutePathString())
f
.absolutePathString()
.removePrefix(directory.absolutePathString())
.dropLast(6) // remove .class
.replace('/', '.')
try {
@@ -59,7 +65,7 @@ internal fun findViews(packageName: String): List<ViewInfo> {
)
}
} catch (e: ClassNotFoundException) {
System.err.println(e)
JewelLogger.getInstance("Views").error(e)
} catch (ignore: InstantiationException) {
// We try to instantiate an interface
// or an object that does not have a

View File

@@ -33,6 +33,7 @@ import org.jetbrains.jewel.foundation.lazy.SelectionMode
import org.jetbrains.jewel.foundation.lazy.rememberSelectableLazyListState
import org.jetbrains.jewel.foundation.lazy.tree.buildTree
import org.jetbrains.jewel.foundation.theme.JewelTheme
import org.jetbrains.jewel.foundation.util.JewelLogger
import org.jetbrains.jewel.samples.standalone.viewmodel.View
import org.jetbrains.jewel.ui.component.Chip
import org.jetbrains.jewel.ui.component.CircularProgressIndicator
@@ -99,7 +100,8 @@ fun SelectableLazyColumnSample() {
Text(
text = listOfItems[index],
modifier =
Modifier.fillMaxWidth()
Modifier
.fillMaxWidth()
.then(
when {
isSelected && isActive -> Modifier.background(Color.Blue)
@@ -107,7 +109,9 @@ fun SelectableLazyColumnSample() {
else -> Modifier
},
).clickable {
println("click on $index")
JewelLogger
.getInstance("ChipsAndTree")
.info("Click on $index")
},
)
}

View File

@@ -15,6 +15,7 @@ import androidx.compose.ui.res.loadSvgPainter
import androidx.compose.ui.res.loadXmlImageVector
import androidx.compose.ui.unit.Density
import org.jetbrains.jewel.foundation.util.inDebugMode
import org.jetbrains.jewel.foundation.util.myLogger
import org.jetbrains.jewel.ui.icon.IconKey
import org.jetbrains.jewel.ui.icon.LocalNewUiChecker
import org.w3c.dom.Document
@@ -50,6 +51,8 @@ public class ResourcePainterProvider(
private val basePath: String,
vararg classLoaders: ClassLoader,
) : PainterProvider {
private val logger = myLogger()
private val classLoaders = classLoaders.toSet()
private val cache = ConcurrentHashMap<Int, Painter>()
@@ -75,25 +78,25 @@ public class ResourcePainterProvider(
val scope = Scope(density, basePath, classLoaders)
val currentHintsProvider = LocalPainterHintsProvider.current
currentHintsProvider.priorityHints(basePath)
currentHintsProvider
.priorityHints(basePath)
.forEach { scope.resolveHint(it) }
hints.forEach { scope.resolveHint(it) }
currentHintsProvider.hints(basePath)
currentHintsProvider
.hints(basePath)
.forEach { scope.resolveHint(it) }
val cacheKey = scope.acceptedHints.hashCode() * 31 + LocalDensity.current.hashCode()
if (inDebugMode && cache[cacheKey] != null) {
println("Cache hit for $basePath (accepted hints: ${scope.acceptedHints.joinToString()})")
if (cache[cacheKey] != null) {
logger.debug("Cache hit for $basePath (accepted hints: ${scope.acceptedHints.joinToString()})")
}
val painter =
cache.getOrPut(cacheKey) {
if (inDebugMode) {
println("Cache miss for $basePath (accepted hints: ${scope.acceptedHints.joinToString()})")
}
logger.debug("Cache miss for $basePath (accepted hints: ${scope.acceptedHints.joinToString()})")
loadPainter(scope)
}
@@ -142,7 +145,7 @@ public class ResourcePainterProvider(
for (classLoader in contextClassLoaders) {
val url = classLoader.getResource(normalized)
if (url != null) {
if (inDebugMode) println("Found resource: '$normalized'")
logger.debug("Found resource: '$normalized'")
return scope to url
}
}
@@ -159,9 +162,7 @@ public class ResourcePainterProvider(
url = url,
loadingAction = { resourceUrl ->
patchSvg(scope, url.openStream(), scope.acceptedHints).use { inputStream ->
if (inDebugMode) {
println("Loading icon $basePath(${scope.acceptedHints.joinToString()}) from $resourceUrl")
}
logger.debug("Loading icon $basePath(${scope.acceptedHints.joinToString()}) from $resourceUrl")
loadSvgPainter(inputStream, scope)
}
},
@@ -186,10 +187,9 @@ public class ResourcePainterProvider(
with(hint) { scope.patch(document.documentElement) }
}
return document.writeToString()
.also { patchedSvg ->
if (inDebugMode) println("Patched SVG:\n\n$patchedSvg")
}
return document
.writeToString()
.also { patchedSvg -> logger.debug("Patched SVG:\n\n$patchedSvg") }
.byteInputStream()
}
}
@@ -234,7 +234,7 @@ public class ResourcePainterProvider(
error(message)
}
System.err.println(message)
logger.error(message)
return errorPainter
}
@@ -247,7 +247,8 @@ public class ResourcePainterProvider(
override val classLoaders: Set<ClassLoader>,
override val path: String = rawPath,
override val acceptedHints: MutableList<PainterHint> = mutableListOf(),
) : ResourcePainterProviderScope, Density by localDensity {
) : ResourcePainterProviderScope,
Density by localDensity {
fun apply(pathHint: PainterPathHint): Scope? {
with(pathHint) {
val patched = patch()
@@ -293,7 +294,7 @@ public fun rememberResourcePainterProvider(
return remember(iconKey, iconClass.classLoader, isNewUi) {
ResourcePainterProvider(
iconKey.path(isNewUi),
iconClass.classLoader
iconClass.classLoader,
)
}
}