mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-330211 use kotlinx serialization for ConsentAttributes, use stream parser for UITheme
GitOrigin-RevId: 168f691910c033df5a90fb6b1e0cb47aada129c2
This commit is contained in:
committed by
intellij-monorepo-bot
parent
c13f33e9ec
commit
75d7c35138
@@ -32,7 +32,6 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.ide.core" exported="" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util.text.matching" />
|
||||
<orderEntry type="module" module-name="intellij.platform.diagnostic" />
|
||||
<orderEntry type="library" name="jackson-jr-objects" level="project" />
|
||||
<orderEntry type="library" name="jackson" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,14 +1,13 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.ide.ui;
|
||||
|
||||
import com.fasterxml.jackson.jr.ob.JSON;
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.intellij.AbstractBundle;
|
||||
import com.intellij.DynamicBundle;
|
||||
import com.intellij.ide.plugins.cl.PluginAwareClassLoader;
|
||||
import com.intellij.ide.ui.laf.UIThemeLookAndFeelInfo;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.IconPathPatcher;
|
||||
import com.intellij.openapi.util.SystemInfoRt;
|
||||
import com.intellij.ui.ColorHexUtil;
|
||||
import com.intellij.ui.Gray;
|
||||
import com.intellij.ui.icons.ImageDataByPathLoader;
|
||||
@@ -31,6 +30,7 @@ import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -44,11 +44,6 @@ public final class UITheme {
|
||||
|
||||
private final UIThemeBean bean;
|
||||
|
||||
private static final String OS_MACOS_KEY = "os.mac";
|
||||
private static final String OS_WINDOWS_KEY = "os.windows";
|
||||
private static final String OS_LINUX_KEY = "os.linux";
|
||||
private static final String OS_DEFAULT_KEY = "os.default";
|
||||
|
||||
private UITheme(@NotNull UIThemeBean bean) {
|
||||
this.bean = bean;
|
||||
}
|
||||
@@ -73,18 +68,9 @@ public final class UITheme {
|
||||
return bean.author;
|
||||
}
|
||||
|
||||
// it caches classes - must be not extracted to util class
|
||||
// .disable(JSON.Feature.PRESERVE_FIELD_ORDERING) - cannot be disabled, for unknown reason order is important,
|
||||
// for example, button label font color for light theme is not white, but black
|
||||
private static final JSON JSON_READER = JSON.builder()
|
||||
.enable(JSON.Feature.READ_ONLY)
|
||||
.disable(JSON.Feature.USE_DEFERRED_MAPS)
|
||||
.disable(JSON.Feature.HANDLE_JAVA_BEANS)
|
||||
.build();
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static @NotNull UITheme loadFromJson(@NotNull InputStream stream, @NotNull @NonNls String themeId) throws IOException {
|
||||
UIThemeBean theme = JSON_READER.beanFrom(UIThemeBean.class, stream);
|
||||
UIThemeBean theme = UIThemeBean.Companion.readTheme(new JsonFactory().createParser(stream));
|
||||
theme.id = themeId;
|
||||
return postProcessTheme(theme, findParentTheme(theme), null, Function.identity());
|
||||
}
|
||||
@@ -93,7 +79,7 @@ public final class UITheme {
|
||||
@NotNull @NonNls String themeId,
|
||||
@Nullable ClassLoader provider,
|
||||
@NotNull Function<? super String, String> iconsMapper) throws IOException {
|
||||
UIThemeBean theme = JSON_READER.beanFrom(UIThemeBean.class, data);
|
||||
UIThemeBean theme = UIThemeBean.Companion.readTheme(new JsonFactory().createParser(data));
|
||||
theme.id = themeId;
|
||||
return postProcessTheme(theme, findParentTheme(theme), provider, iconsMapper);
|
||||
}
|
||||
@@ -105,7 +91,7 @@ public final class UITheme {
|
||||
@NotNull Function<? super String, String> iconsMapper,
|
||||
@Nullable UITheme defaultDarkParent,
|
||||
@Nullable UITheme defaultLightParent) throws IOException {
|
||||
UIThemeBean theme = JSON_READER.beanFrom(UIThemeBean.class, data);
|
||||
UIThemeBean theme = UIThemeBean.Companion.readTheme(new JsonFactory().createParser(data));
|
||||
theme.id = themeId;
|
||||
if (theme.dark && parentTheme == null) {
|
||||
return postProcessTheme(theme, defaultDarkParent, provider, iconsMapper);
|
||||
@@ -120,11 +106,9 @@ public final class UITheme {
|
||||
@Nullable UITheme parentTheme,
|
||||
@Nullable ClassLoader provider,
|
||||
@NotNull Function<? super String, String> iconsMapper) throws IllegalStateException {
|
||||
normalizeKeyPaths(theme);
|
||||
if (parentTheme != null) {
|
||||
UIThemeBean.Companion.importFromParentTheme(theme, parentTheme.bean);
|
||||
}
|
||||
UIThemeBean.Companion.putDefaultsIfAbsent(theme);
|
||||
return new UITheme(loadFromJson(theme, provider, iconsMapper));
|
||||
}
|
||||
|
||||
@@ -143,60 +127,6 @@ public final class UITheme {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten
|
||||
* <pre>{@code "Editor": { "SearchField" : { "borderInsets" : "7,10,7,8" } }}</pre>
|
||||
* to
|
||||
* <pre>{@code "Editor.SearchField.borderInsets" : "7,10,7,8"}</pre>
|
||||
* in internal representation.
|
||||
* <p>
|
||||
* We also resolve per-OS keys here:
|
||||
* <pre> {@code
|
||||
* "Menu.borderColor": {
|
||||
* "os.default": "Grey12",
|
||||
* "os.windows": "Blue12"
|
||||
* }
|
||||
* }</pre>
|
||||
* <p>
|
||||
* This is helpful when we need to check if some key was already set in {@link UIThemeBean.Companion#putDefaultsIfAbsent},
|
||||
* and to make overriding parentTheme keys independent of used form.
|
||||
* <p>
|
||||
* NB: we intentionally do not expand "*" patterns here.
|
||||
*/
|
||||
private static void normalizeKeyPaths(@NotNull UIThemeBean theme) {
|
||||
if (theme.ui == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, ?> entry : theme.ui.entrySet()) {
|
||||
normalizeKeyValue(entry.getKey(), entry.getValue(), result);
|
||||
}
|
||||
theme.ui = result;
|
||||
}
|
||||
|
||||
private static void normalizeKeyValue(@NotNull String keyPrefix,
|
||||
@NotNull Object value,
|
||||
@NotNull Map<String, Object> result) {
|
||||
if (value instanceof Map) {
|
||||
@SuppressWarnings("unchecked") Map<String, Object> valueMap = (Map<String, Object>)value;
|
||||
|
||||
Object osValue = getOSCustomization(valueMap);
|
||||
if (osValue == null) {
|
||||
for (Map.Entry<String, Object> entry : valueMap.entrySet()) {
|
||||
String uiKey = createUIKey(keyPrefix, entry.getKey());
|
||||
normalizeKeyValue(uiKey, entry.getValue(), result);
|
||||
}
|
||||
}
|
||||
else {
|
||||
normalizeKeyValue(keyPrefix, osValue, result);
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.put(keyPrefix, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static @NotNull UIThemeBean loadFromJson(@NotNull UIThemeBean theme,
|
||||
@Nullable ClassLoader provider,
|
||||
@NotNull Function<? super String, String> iconsMapper) throws IllegalStateException {
|
||||
@@ -422,7 +352,7 @@ public final class UITheme {
|
||||
return bean.editorScheme;
|
||||
}
|
||||
|
||||
public String @Nullable [] getAdditionalEditorSchemes() {
|
||||
public @Nullable List<String> getAdditionalEditorSchemes() {
|
||||
return bean.additionalEditorSchemes;
|
||||
}
|
||||
|
||||
@@ -530,18 +460,6 @@ public final class UITheme {
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable Object getOSCustomization(@NotNull Map<String, Object> map) {
|
||||
String osKey = SystemInfoRt.isWindows ? OS_WINDOWS_KEY :
|
||||
SystemInfoRt.isMac ? OS_MACOS_KEY :
|
||||
SystemInfoRt.isLinux ? OS_LINUX_KEY : null;
|
||||
if (osKey != null && map.containsKey(osKey)) {
|
||||
return map.get(osKey);
|
||||
}
|
||||
else {
|
||||
return map.get(OS_DEFAULT_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void addPattern(String key, Object value, UIDefaults defaults) {
|
||||
Object o = defaults.get("*");
|
||||
|
||||
@@ -1,35 +1,20 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:Suppress("ReplacePutWithAssignment")
|
||||
@file:Suppress("ReplacePutWithAssignment", "ReplaceGetOrSet")
|
||||
|
||||
package com.intellij.ide.ui
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser
|
||||
import com.fasterxml.jackson.core.JsonToken
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.util.IconPathPatcher
|
||||
import com.intellij.ui.ExperimentalUI.Companion.isNewUI
|
||||
import com.intellij.openapi.util.SystemInfoRt
|
||||
import com.intellij.ui.ExperimentalUI
|
||||
import com.intellij.util.SVGLoader.SvgElementColorPatcherProvider
|
||||
import java.util.*
|
||||
import java.util.function.BiFunction
|
||||
|
||||
internal class UIThemeBean {
|
||||
companion object {
|
||||
/**
|
||||
* Ensure that the old themes are not missing some vital keys.
|
||||
*
|
||||
* We are patching them here instead of using [com.intellij.ui.JBColor.namedColor] fallback
|
||||
* to make sure [javax.swing.UIManager.getColor] works properly.
|
||||
*/
|
||||
fun putDefaultsIfAbsent(theme: UIThemeBean) {
|
||||
var ui = theme.ui
|
||||
if (ui == null) {
|
||||
ui = LinkedHashMap()
|
||||
theme.ui = ui
|
||||
}
|
||||
if (isNewUI()) {
|
||||
ui.putIfAbsent("EditorTabs.underlineArc", "4")
|
||||
|
||||
// require theme to specify ToolWindow stripe button colors explicitly, without "*"
|
||||
ui.putIfAbsent("ToolWindow.Button.selectedBackground", "#3573F0")
|
||||
ui.putIfAbsent("ToolWindow.Button.selectedForeground", "#FFFFFF")
|
||||
}
|
||||
}
|
||||
|
||||
fun importFromParentTheme(theme: UIThemeBean, parentTheme: UIThemeBean) {
|
||||
theme.ui = importMapFromParentTheme(theme.ui, parentTheme.ui)
|
||||
theme.icons = importMapFromParentTheme(theme.icons, parentTheme.icons)
|
||||
@@ -39,7 +24,8 @@ internal class UIThemeBean {
|
||||
theme.iconColorsOnSelection = importMapFromParentTheme(theme.iconColorsOnSelection, parentTheme.iconColorsOnSelection)
|
||||
}
|
||||
|
||||
private fun importMapFromParentTheme(themeMap: MutableMap<String, Any?>?, parentThemeMap: Map<String, Any?>?): MutableMap<String, Any?>? {
|
||||
private fun importMapFromParentTheme(themeMap: MutableMap<String, Any?>?,
|
||||
parentThemeMap: Map<String, Any?>?): MutableMap<String, Any?>? {
|
||||
if (parentThemeMap == null) {
|
||||
return themeMap
|
||||
}
|
||||
@@ -53,56 +39,338 @@ internal class UIThemeBean {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun readTheme(parser: JsonParser): UIThemeBean {
|
||||
check(parser.nextToken() == JsonToken.START_OBJECT)
|
||||
val bean = UIThemeBean()
|
||||
while (true) {
|
||||
when (parser.nextToken()) {
|
||||
JsonToken.START_OBJECT -> {
|
||||
val fieldName = parser.currentName()
|
||||
// ordered map is required (not clear why)
|
||||
val map = LinkedHashMap<String, Any?>()
|
||||
readFlatMapFromJson(parser, map)
|
||||
when (fieldName) {
|
||||
"icons" -> bean.icons = map
|
||||
"background" -> bean.background = map
|
||||
"emptyFrameBackground" -> bean.emptyFrameBackground = map
|
||||
"colors" -> bean.colors = map
|
||||
"iconColorsOnSelection" -> bean.iconColorsOnSelection = map
|
||||
"ui" -> {
|
||||
putDefaultsIfAbsent(map)
|
||||
bean.ui = map
|
||||
}
|
||||
"UIDesigner" -> {
|
||||
parser.skipChildren()
|
||||
}
|
||||
else -> {
|
||||
logger<UIThemeBean>().warn("Unknown field: $fieldName")
|
||||
}
|
||||
}
|
||||
}
|
||||
JsonToken.END_OBJECT -> {
|
||||
}
|
||||
JsonToken.START_ARRAY -> {
|
||||
val fieldName = parser.currentName()
|
||||
val list = ArrayList<String>()
|
||||
while (parser.nextToken() != JsonToken.END_ARRAY) {
|
||||
when (parser.currentToken()) {
|
||||
JsonToken.VALUE_STRING -> {
|
||||
list.add(parser.valueAsString)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
when (fieldName) {
|
||||
"additionalEditorSchemes" -> {
|
||||
bean.additionalEditorSchemes = list
|
||||
}
|
||||
else -> {
|
||||
logger<UIThemeBean>().warn("Unknown field: ${parser.currentName()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
JsonToken.VALUE_STRING -> {
|
||||
when (parser.currentName()) {
|
||||
"id" -> bean.id = parser.valueAsString
|
||||
"name" -> bean.name = parser.valueAsString
|
||||
"nameKey" -> bean.nameKey = parser.valueAsString
|
||||
"parentTheme" -> bean.parentTheme = parser.valueAsString
|
||||
"resourceBundle" -> bean.resourceBundle = parser.valueAsString
|
||||
"author" -> bean.author = parser.valueAsString
|
||||
|
||||
"editorScheme" -> bean.editorScheme = parser.valueAsString
|
||||
"editorSchemeName" -> bean.editorSchemeName = parser.valueAsString
|
||||
}
|
||||
}
|
||||
JsonToken.VALUE_TRUE -> readTopLevelBoolean(parser, bean, value = true)
|
||||
JsonToken.VALUE_FALSE -> readTopLevelBoolean(parser, bean, value = false)
|
||||
JsonToken.FIELD_NAME -> {
|
||||
}
|
||||
null -> break
|
||||
else -> {
|
||||
logger<UIThemeBean>().warn("Unknown field: ${parser.currentName()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
putDefaultsIfAbsent(bean)
|
||||
return bean
|
||||
}
|
||||
|
||||
private fun readTopLevelBoolean(parser: JsonParser, bean: UIThemeBean, value: Boolean) {
|
||||
when (parser.currentName()) {
|
||||
"dark" -> bean.dark = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Transient
|
||||
@JvmField
|
||||
var providerClassLoader: ClassLoader? = null
|
||||
|
||||
@JvmField
|
||||
var name: String? = null
|
||||
@JvmField
|
||||
var dark = false
|
||||
@JvmField
|
||||
var author: String? = null
|
||||
@JvmField
|
||||
var id: String? = null
|
||||
|
||||
@JvmField
|
||||
var editorScheme: String? = null
|
||||
var name: String? = null
|
||||
|
||||
@JvmField
|
||||
var nameKey: String? = null
|
||||
|
||||
@JvmField
|
||||
var parentTheme: String? = null
|
||||
|
||||
@JvmField
|
||||
var additionalEditorSchemes: Array<String>? = null
|
||||
var resourceBundle: String? = "messages.IdeBundle"
|
||||
|
||||
@JvmField
|
||||
var author: String? = null
|
||||
|
||||
@JvmField
|
||||
var editorScheme: String? = null
|
||||
|
||||
@JvmField
|
||||
var editorSchemeName: String? = null
|
||||
|
||||
@JvmField
|
||||
var dark: Boolean = false
|
||||
|
||||
@JvmField
|
||||
var additionalEditorSchemes: List<String>? = null
|
||||
|
||||
@JvmField
|
||||
var ui: MutableMap<String, Any?>? = null
|
||||
|
||||
@JvmField
|
||||
var icons: MutableMap<String, Any?>? = null
|
||||
|
||||
@JvmField
|
||||
var background: MutableMap<String, Any?>? = null
|
||||
|
||||
@JvmField
|
||||
var emptyFrameBackground: MutableMap<String, Any?>? = null
|
||||
|
||||
@JvmField
|
||||
var colors: MutableMap<String, Any?>? = null
|
||||
|
||||
@JvmField
|
||||
var iconColorsOnSelection: MutableMap<String, Any?>? = null
|
||||
|
||||
@JvmField
|
||||
@Transient
|
||||
var patcher: IconPathPatcher? = null
|
||||
|
||||
@JvmField
|
||||
var background: MutableMap<String, Any?>? = null
|
||||
@JvmField
|
||||
var emptyFrameBackground: MutableMap<String, Any?>? = null
|
||||
@JvmField
|
||||
var colors: MutableMap<String, Any?>? = null
|
||||
@JvmField
|
||||
var iconColorsOnSelection: MutableMap<String, Any?>? = null
|
||||
|
||||
@JvmField
|
||||
var editorSchemeName: String? = null
|
||||
|
||||
@JvmField
|
||||
@Transient
|
||||
var colorPatcher: SvgElementColorPatcherProvider? = null
|
||||
|
||||
@JvmField
|
||||
@Transient
|
||||
var selectionColorPatcher: SvgElementColorPatcherProvider? = null
|
||||
}
|
||||
|
||||
@JvmField
|
||||
var resourceBundle: String? = "messages.IdeBundle"
|
||||
@JvmField
|
||||
var nameKey: String? = null
|
||||
}
|
||||
/**
|
||||
* Flatten example: `"Editor": { "SearchField": { "borderInsets": "7,10,7,8" } }` is flattened to
|
||||
* `"Editor.SearchField.borderInsets": "7,10,7,8` in internal representation.
|
||||
*
|
||||
* Per-OS keys are also resolved as shown below:
|
||||
* ```json
|
||||
* "Menu.borderColor": {
|
||||
* "os.default": "Grey12",
|
||||
* "os.windows": "Blue12"
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* This is useful when we need to validate if a certain key was already set in [putDefaultsIfAbsent],
|
||||
* and to uniformly override parentTheme keys regardless of used format.
|
||||
*
|
||||
* Note: we intentionally do not expand "*" patterns here.
|
||||
*/
|
||||
private fun readFlatMapFromJson(parser: JsonParser, result: MutableMap<String, Any?>) {
|
||||
check(parser.currentToken() == JsonToken.START_OBJECT)
|
||||
|
||||
val prefix = ArrayDeque<String>()
|
||||
val path = StringBuilder()
|
||||
var currentFieldName: String? = null
|
||||
var level = 1
|
||||
while (true) {
|
||||
when (parser.nextToken()) {
|
||||
JsonToken.START_OBJECT -> {
|
||||
level++
|
||||
prefix.addLast(currentFieldName!!)
|
||||
currentFieldName = null
|
||||
}
|
||||
JsonToken.END_OBJECT -> {
|
||||
level--
|
||||
prefix.pollLast()
|
||||
currentFieldName = null
|
||||
|
||||
if (level == 0) {
|
||||
assert(prefix.isEmpty())
|
||||
break
|
||||
}
|
||||
}
|
||||
JsonToken.START_ARRAY -> {
|
||||
val fieldName = parser.currentName()
|
||||
while (true) {
|
||||
when (parser.nextToken()) {
|
||||
JsonToken.END_ARRAY -> break
|
||||
JsonToken.VALUE_STRING -> {
|
||||
if (!prefix.isEmpty()) {
|
||||
prefix.joinTo(buffer = path, separator = "/")
|
||||
path.append('.')
|
||||
}
|
||||
path.append(fieldName)
|
||||
result.put(path.toString(), parser.text)
|
||||
path.setLength(0)
|
||||
}
|
||||
else -> {
|
||||
logError(parser)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
JsonToken.VALUE_STRING -> {
|
||||
putEntry(prefix, result, parser, path) { parser.text }
|
||||
}
|
||||
JsonToken.VALUE_NUMBER_INT -> {
|
||||
putEntry(prefix, result, parser, path) { parser.intValue }
|
||||
}
|
||||
JsonToken.VALUE_NUMBER_FLOAT -> {
|
||||
putEntry(prefix, result, parser, path) { parser.doubleValue }
|
||||
}
|
||||
JsonToken.VALUE_FALSE -> {
|
||||
putEntry(prefix, result, parser, path) { false }
|
||||
}
|
||||
JsonToken.VALUE_TRUE -> {
|
||||
putEntry(prefix, result, parser, path) { true }
|
||||
}
|
||||
JsonToken.VALUE_NULL -> {
|
||||
}
|
||||
JsonToken.FIELD_NAME -> {
|
||||
currentFieldName = parser.currentName()
|
||||
}
|
||||
null -> {
|
||||
break
|
||||
}
|
||||
else -> {
|
||||
logError(parser)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.replaceAll(BiFunction { _, u -> if (u is OsDefaultValue) u.v else u })
|
||||
}
|
||||
|
||||
private const val OS_MACOS_KEY = "os.mac"
|
||||
private const val OS_WINDOWS_KEY = "os.windows"
|
||||
private const val OS_LINUX_KEY = "os.linux"
|
||||
private const val OS_DEFAULT_KEY = "os.default"
|
||||
|
||||
private val osKey = when {
|
||||
SystemInfoRt.isWindows -> OS_WINDOWS_KEY
|
||||
SystemInfoRt.isMac -> OS_MACOS_KEY
|
||||
else -> OS_LINUX_KEY
|
||||
}
|
||||
|
||||
private fun putEntry(prefix: Deque<String>,
|
||||
result: MutableMap<String, Any?>,
|
||||
parser: JsonParser,
|
||||
path: StringBuilder,
|
||||
getter: () -> Any?) {
|
||||
if (!prefix.isEmpty()) {
|
||||
prefix.joinTo(buffer = path, separator = ".")
|
||||
}
|
||||
|
||||
val key = parser.currentName()
|
||||
val value = getter()
|
||||
when (key) {
|
||||
osKey -> {
|
||||
}
|
||||
OS_WINDOWS_KEY, OS_MACOS_KEY, OS_LINUX_KEY -> {
|
||||
path.setLength(0)
|
||||
return
|
||||
}
|
||||
OS_DEFAULT_KEY -> {
|
||||
val compositeKey = path.toString()
|
||||
path.setLength(0)
|
||||
|
||||
val oldValue = result.putIfAbsent(compositeKey, OsDefaultValue(value))
|
||||
if (oldValue is OsDefaultValue) {
|
||||
logger<UIThemeBean>().error("Duplicated value: (value=$value, compositeKey=$compositeKey)")
|
||||
}
|
||||
return
|
||||
}
|
||||
"UI" -> {
|
||||
path.append(key)
|
||||
}
|
||||
else -> {
|
||||
if (path.isNotEmpty()) {
|
||||
path.append('.')
|
||||
}
|
||||
path.append(key)
|
||||
}
|
||||
}
|
||||
|
||||
result.put(path.toString(), value)
|
||||
path.setLength(0)
|
||||
}
|
||||
|
||||
private class OsDefaultValue(@JvmField val v: Any?)
|
||||
|
||||
private fun logError(parser: JsonParser) {
|
||||
logger<UIThemeBean>().warn("JSON contains data in unsupported format (token=${parser.currentToken}): ${parser.currentValue()}")
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the old themes are not missing some vital keys.
|
||||
*
|
||||
* We are patching them here instead of using [com.intellij.ui.JBColor.namedColor] fallback
|
||||
* to make sure [javax.swing.UIManager.getColor] works properly.
|
||||
*/
|
||||
private fun putDefaultsIfAbsent(theme: UIThemeBean) {
|
||||
if (!ExperimentalUI.isNewUI()) {
|
||||
return
|
||||
}
|
||||
|
||||
var ui = theme.ui
|
||||
if (ui == null) {
|
||||
ui = LinkedHashMap()
|
||||
theme.ui = ui
|
||||
putDefaultsIfAbsent(ui)
|
||||
}
|
||||
}
|
||||
|
||||
private fun putDefaultsIfAbsent(ui: MutableMap<String, Any?>) {
|
||||
if (!ExperimentalUI.isNewUI()) {
|
||||
return
|
||||
}
|
||||
|
||||
ui.putIfAbsent("EditorTabs.underlineArc", "4")
|
||||
|
||||
// require theme to specify ToolWindow stripe button colors explicitly, without "*"
|
||||
ui.putIfAbsent("ToolWindow.Button.selectedBackground", "#3573F0")
|
||||
ui.putIfAbsent("ToolWindow.Button.selectedForeground", "#FFFFFF")
|
||||
}
|
||||
|
||||
@@ -119,7 +119,6 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.statistics.uploader" />
|
||||
<orderEntry type="library" name="zip-signer" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util.zip" />
|
||||
<orderEntry type="library" name="jackson-jr-objects" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.remote.core" exported="" />
|
||||
<orderEntry type="library" name="icu4j" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util.xmlDom" />
|
||||
|
||||
@@ -233,9 +233,11 @@ private suspend fun preInitApp(app: ApplicationImpl,
|
||||
|
||||
euaTaskDeferred?.await()?.invoke()
|
||||
|
||||
val installIconPatcherJob = if (loadIconMapping == null) null else launch {
|
||||
loadIconMapping.join()
|
||||
ExperimentalUI.getInstance().installIconPatcher()
|
||||
if (loadIconMapping != null) {
|
||||
launch {
|
||||
loadIconMapping.join()
|
||||
ExperimentalUI.getInstance().installIconPatcher()
|
||||
}
|
||||
}
|
||||
|
||||
coroutineScope {
|
||||
|
||||
@@ -1,15 +1,55 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.ide.gdpr;
|
||||
package com.intellij.ide.gdpr
|
||||
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
/**
|
||||
* Data structure describing all possible Consent JSON attributes
|
||||
*/
|
||||
final class ConsentAttributes {
|
||||
public String consentId;
|
||||
public String version;
|
||||
public String text;
|
||||
public String printableName;
|
||||
public boolean accepted;
|
||||
public boolean deleted;
|
||||
public long acceptanceTime;
|
||||
@Serializable
|
||||
internal class ConsentAttributes {
|
||||
companion object {
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
private val jsonConfig by lazy {
|
||||
Json {
|
||||
ignoreUnknownKeys = true
|
||||
isLenient = true
|
||||
encodeDefaults = false
|
||||
prettyPrint = true
|
||||
prettyPrintIndent = " "
|
||||
}
|
||||
}
|
||||
|
||||
fun readListFromJson(json: String): List<ConsentAttributes> {
|
||||
return jsonConfig.decodeFromString<List<ConsentAttributes>>(json)
|
||||
}
|
||||
|
||||
fun writeListToJson(list: List<ConsentAttributes>): String {
|
||||
return jsonConfig.encodeToString<List<ConsentAttributes>>(list)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmField
|
||||
var consentId: String? = null
|
||||
|
||||
@JvmField
|
||||
var version: String? = null
|
||||
|
||||
@JvmField
|
||||
var text: String? = null
|
||||
|
||||
@JvmField
|
||||
var printableName: String? = null
|
||||
|
||||
@JvmField
|
||||
var accepted: Boolean = false
|
||||
|
||||
@JvmField
|
||||
var deleted: Boolean = false
|
||||
|
||||
@JvmField
|
||||
var acceptanceTime: Long = 0
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.ide.gdpr;
|
||||
|
||||
import com.fasterxml.jackson.jr.ob.JSON;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.intellij.diagnostic.LoadingState;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ApplicationNamesInfo;
|
||||
@@ -424,14 +421,16 @@ public final class ConsentOptions {
|
||||
}
|
||||
|
||||
private @NotNull Collection<ConsentAttributes> fromJson(@Nullable String json) {
|
||||
if (json == null || json.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
try {
|
||||
List<ConsentAttributes> data = json == null || json.isEmpty() ? null : JSON.std.listOfFrom(ConsentAttributes.class, json);
|
||||
if (data != null) {
|
||||
for (ConsentAttributes attributes : data) {
|
||||
attributes.consentId = lookupConsentID(attributes.consentId);
|
||||
}
|
||||
return data;
|
||||
List<ConsentAttributes> data = ConsentAttributes.Companion.readListFromJson(json);
|
||||
for (ConsentAttributes attributes : data) {
|
||||
attributes.consentId = lookupConsentID(attributes.consentId);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.info(e);
|
||||
@@ -440,15 +439,14 @@ public final class ConsentOptions {
|
||||
}
|
||||
|
||||
private @NotNull String consentsToJson(@NotNull Stream<Consent> consents) {
|
||||
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
|
||||
return gson.toJson(consents.map(consent -> {
|
||||
return ConsentAttributes.Companion.writeListToJson(consents.map(consent -> {
|
||||
final ConsentAttributes attribs = consent.toConsentAttributes();
|
||||
final String prefix = getProductConsentKind(myProductCode, attribs.consentId);
|
||||
if (prefix != null) {
|
||||
attribs.consentId = prefix;
|
||||
}
|
||||
return attribs;
|
||||
}).toArray());
|
||||
}).toList());
|
||||
}
|
||||
|
||||
private static @NotNull String confirmedConsentToExternalString(@NotNull Stream<ConfirmedConsent> consents) {
|
||||
|
||||
@@ -138,11 +138,12 @@ private fun readDataFromJson(parser: JsonParser, result: MutableMap<String, Stri
|
||||
}
|
||||
|
||||
private fun addWithCheck(result: MutableMap<String, String>, parser: JsonParser, path: StringBuilder) {
|
||||
val oldValue = result.get(parser.text)
|
||||
if (oldValue != null && oldValue != path.toString()) {
|
||||
logger<IconMapLoader>().error("Double icon mapping: ${parser.text} -> $oldValue or $path")
|
||||
val key = parser.text
|
||||
val p = path.toString()
|
||||
val oldValue = result.put(key, p)
|
||||
if (oldValue != null && oldValue != p) {
|
||||
logger<IconMapLoader>().error("Double icon mapping: $key -> $oldValue or $path")
|
||||
}
|
||||
result.put(parser.text, path.toString())
|
||||
}
|
||||
|
||||
private fun logError(parser: JsonParser) {
|
||||
|
||||
+1
-1
@@ -319,7 +319,7 @@ public final class EditorColorsManagerImpl extends EditorColorsManager implement
|
||||
|
||||
for (UIThemeLookAndFeelInfo laf : UiThemeProviderListManager.Companion.getInstance().getLaFs()) {
|
||||
UITheme theme = laf.getTheme();
|
||||
String[] schemes = theme.getAdditionalEditorSchemes();
|
||||
List<String> schemes = theme.getAdditionalEditorSchemes();
|
||||
PluginDescriptor pluginDescriptor = getPluginDescriptor(theme);
|
||||
if (schemes != null) {
|
||||
for (String scheme : schemes) {
|
||||
|
||||
Reference in New Issue
Block a user