From 8e7f065479a5fbac6a4ddb47d727ccf8b421f1e2 Mon Sep 17 00:00:00 2001 From: Niklas Rentz Date: Tue, 17 Oct 2023 15:49:36 +0200 Subject: [PATCH 01/10] added support for initializing a color theme for supporting dark theme. --- .../klighd/lsp/KGraphDiagramState.xtend | 6 ++ .../klighd/lsp/KGraphDiagramUpdater.xtend | 5 + .../lsp/KGraphLanguageServerExtension.xtend | 3 + .../de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend | 40 ++++++++ .../kieler/klighd/util/ColorPreferences.java | 98 +++++++++++++++++++ .../kieler/klighd/util/KlighdProperties.java | 6 ++ 6 files changed, 158 insertions(+) create mode 100644 plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorPreferences.java diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramState.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramState.xtend index 586d85d0b..ab9506532 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramState.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramState.xtend @@ -26,6 +26,7 @@ import de.cau.cs.kieler.klighd.internal.ISynthesis import de.cau.cs.kieler.klighd.kgraph.KGraphElement import de.cau.cs.kieler.klighd.krendering.KImage import de.cau.cs.kieler.klighd.lsp.model.ImageData +import de.cau.cs.kieler.klighd.util.ColorPreferences import java.net.URLDecoder import java.util.HashMap import java.util.HashSet @@ -106,6 +107,11 @@ class KGraphDiagramState { */ JsonElement clientOptions + /** + * The color preferences as configured by the client. + */ + public ColorPreferences colorPreferences + /** * A map to map the Sprotty client id to the URI leading to the resource. */ diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramUpdater.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramUpdater.xtend index 9755d54f2..ef3e1568d 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramUpdater.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramUpdater.xtend @@ -30,6 +30,7 @@ import de.cau.cs.kieler.klighd.lsp.launch.AbstractLanguageServer import de.cau.cs.kieler.klighd.lsp.model.RequestDiagramPieceAction import de.cau.cs.kieler.klighd.lsp.model.SKGraph import de.cau.cs.kieler.klighd.lsp.utils.KGraphMappingUtil +import de.cau.cs.kieler.klighd.util.KlighdProperties import de.cau.cs.kieler.klighd.util.KlighdSynthesisProperties import java.util.HashSet import java.util.List @@ -257,6 +258,10 @@ class KGraphDiagramUpdater extends DiagramUpdater { } } + synchronized (diagramState) { + viewContext.setProperty(KlighdProperties.COLOR_PREFERENCES, diagramState.colorPreferences) + } + val vc = viewContext // Update the model and with that call the diagram synthesis. AbstractLanguageServer.addToMainThreadQueue([ diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphLanguageServerExtension.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphLanguageServerExtension.xtend index 087b62b7b..c52205629 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphLanguageServerExtension.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphLanguageServerExtension.xtend @@ -110,6 +110,8 @@ class KGraphLanguageServerExtension extends SyncDiagramLanguageServer */ public static String CLIENT_DIAGRAM_OPTIONS_PROPERTY = "clientDiagramOptions" + public static String CLIENT_COLOR_PREFERNENCES = "clientColorPreferences" + override initialize(InitializeParams params) { // Close all diagram servers still open from a previous session. val oldClientIds = diagramServerManager.diagramServers.map[ clientId ].toList // toList to avoid lazy evaluation @@ -118,6 +120,7 @@ class KGraphLanguageServerExtension extends SyncDiagramLanguageServer if (initializationOptions instanceof JsonObject) { synchronized (diagramState) { diagramState.clientOptions = initializationOptions.get(CLIENT_DIAGRAM_OPTIONS_PROPERTY) + diagramState.colorPreferences = LSPUtil.parseColorPreferences(initializationOptions.get(CLIENT_COLOR_PREFERNENCES)) } } return super.initialize(params) diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend index b69faaf60..1ad5b9fad 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend @@ -17,8 +17,13 @@ package de.cau.cs.kieler.klighd.lsp import com.google.common.html.HtmlEscapers +import com.google.gson.JsonElement import de.cau.cs.kieler.klighd.ViewContext import de.cau.cs.kieler.klighd.kgraph.KNode +import de.cau.cs.kieler.klighd.krendering.KColor +import de.cau.cs.kieler.klighd.krendering.KRenderingFactory +import de.cau.cs.kieler.klighd.util.ColorPreferences +import java.awt.Color /** * Utility methods for graphs in a language server context. @@ -80,4 +85,39 @@ class LSPUtil { // Replace tabs with four spaces. .replace("\t", "    ") } + + + + /** + * Parses a jsonElement for color preferences from the client. unreadable colors are defaulted to black and white. + */ + static def ColorPreferences parseColorPreferences(JsonElement jsonColors) { + if (!jsonColors.isJsonObject) return null + val foreground = jsonColors.asJsonObject.get("foreground") + val background = jsonColors.asJsonObject.get("background") + val highlight = jsonColors.asJsonObject.get("highlight") + val foregroundColor = parseColor(foreground) + val backgroundColor = parseColor(background) + val highlightColor = parseColor(highlight) + + return new ColorPreferences(foregroundColor, backgroundColor, highlightColor) + + } + + /** + * Parses a single color string in the form #RRGGBB into a KColor, or null if the string is unparsable. + */ + static def KColor parseColor(JsonElement jsonColor) { + if (!jsonColor.isJsonPrimitive) return null + var KColor color = null + try { + val awtColor = Color.decode(jsonColor.asString) + color = KRenderingFactory.eINSTANCE.createKColor + color.red = awtColor.red + color.green = awtColor.green + color.blue = awtColor.blue + } catch (NumberFormatException e) {} + + return color + } } diff --git a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorPreferences.java b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorPreferences.java new file mode 100644 index 000000000..baec5e5b9 --- /dev/null +++ b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorPreferences.java @@ -0,0 +1,98 @@ +/* + * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient + * + * http://rtsys.informatik.uni-kiel.de/kieler + * + * Copyright 2023 by + * + Kiel University + * + Department of Computer Science + * + Real-Time and Embedded Systems Group + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + */ +package de.cau.cs.kieler.klighd.util; + +import de.cau.cs.kieler.klighd.krendering.KColor; +import de.cau.cs.kieler.klighd.krendering.KRenderingFactory; + +/** + * Color preferences for KLighD syntheses. May provide information about the foreground, background, and a highlighting + * color, that may be set by a frontend. + * + * @author nre + */ +public class ColorPreferences { + + /** + * The foreground color, to be used e.g. for texts, lines, etc., or as a reference. + */ + private KColor foreground; + + /** + * The backgrond color to be used as the background, fill color, etc., or as a reference. + */ + private KColor background; + + /** + * A special highlighting color to be used for special renderings, or as a reference. + */ + private KColor highlight; + + /** + * Default constructor for color preferences, setting foreground and highlight to black, background to white. + */ + public ColorPreferences() { + foreground = KRenderingFactory.eINSTANCE.createKColor(); + foreground.setRed(0); + foreground.setGreen(0); + foreground.setBlue(0); + + background = KRenderingFactory.eINSTANCE.createKColor(); + background.setRed(255); + background.setGreen(255); + background.setBlue(255); + + highlight = KRenderingFactory.eINSTANCE.createKColor(); + highlight.setRed(0); + highlight.setGreen(0); + highlight.setBlue(0); + } + + /** + * Creates a new ColorPreferences with the given colors, or the default colors if some parameters are {@code null}. + * + * @param foreground + * @param background + * @param highlight + */ + public ColorPreferences(KColor foreground, KColor background, KColor highlight) { + if (foreground == null) { + foreground = KRenderingFactory.eINSTANCE.createKColor(); + foreground.setRed(0); + foreground.setGreen(0); + foreground.setBlue(0); + } else { + this.foreground = foreground; + } + if (background == null) { + background = KRenderingFactory.eINSTANCE.createKColor(); + background.setRed(255); + background.setGreen(255); + background.setBlue(255); + } else { + this.background = background; + } + if (highlight == null) { + highlight = KRenderingFactory.eINSTANCE.createKColor(); + highlight.setRed(0); + highlight.setGreen(0); + highlight.setBlue(0); + } else { + this.highlight = highlight; + } + } +} diff --git a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/KlighdProperties.java b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/KlighdProperties.java index fb3d6d5c6..614f8d969 100644 --- a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/KlighdProperties.java +++ b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/KlighdProperties.java @@ -381,6 +381,12 @@ public static boolean isSelectable(final EObject viewElement) { public static final IProperty IS_NODE_TITLE = new Property("klighd.isNodeTitle", false); + /** + * Property determining the custom color theme to be used by syntheses. + */ + public static final IProperty COLOR_PREFERENCES = + new Property("klighd.colorPreferences", new ColorPreferences()); + /** * Property determining whether this node should be rendered as a proxy. */ From 3816b0c5c86fd61fdf03c8d01b51d9d8a1a9a423 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Tue, 17 Oct 2023 16:25:25 +0200 Subject: [PATCH 02/10] Added getters for ColorPreferences --- .../kieler/klighd/util/ColorPreferences.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorPreferences.java b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorPreferences.java index baec5e5b9..8608a7333 100644 --- a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorPreferences.java +++ b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorPreferences.java @@ -33,7 +33,7 @@ public class ColorPreferences { private KColor foreground; /** - * The backgrond color to be used as the background, fill color, etc., or as a reference. + * The background color to be used as the background, fill color, etc., or as a reference. */ private KColor background; @@ -95,4 +95,25 @@ public ColorPreferences(KColor foreground, KColor background, KColor highlight) this.highlight = highlight; } } + + /** + * @return the foreground color + */ + public KColor getForeground() { + return foreground; + } + + /** + * @return the background color + */ + public KColor getBackground() { + return background; + } + + /** + * @return the highlight color + */ + public KColor getHighlight() { + return highlight; + } } From 234aa4016b0ebf3ecb78976df36b293dc9622d82 Mon Sep 17 00:00:00 2001 From: Niklas Rentz Date: Tue, 17 Oct 2023 20:06:47 +0200 Subject: [PATCH 03/10] klighd.lsp: dark mode handling for more cases for both CLI and VS Code --- .../klighd/lsp/KGraphDiagramServer.xtend | 30 +++++++++++++++++++ .../de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend | 15 ++++++---- .../gson_utils/KGraphTypeAdapterUtil.xtend | 2 ++ .../cs/kieler/klighd/lsp/model/Actions.xtend | 30 +++++++++++++++++++ 4 files changed, 72 insertions(+), 5 deletions(-) diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramServer.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramServer.xtend index aad234347..1c1ac48ff 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramServer.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramServer.xtend @@ -31,6 +31,7 @@ import de.cau.cs.kieler.klighd.lsp.interactive.rectpacking.RectpackingInteractiv import de.cau.cs.kieler.klighd.lsp.launch.AbstractLanguageServer import de.cau.cs.kieler.klighd.lsp.model.CheckImagesAction import de.cau.cs.kieler.klighd.lsp.model.CheckedImagesAction +import de.cau.cs.kieler.klighd.lsp.model.ClientColorPreferencesAction import de.cau.cs.kieler.klighd.lsp.model.DisplayedActionUIData import de.cau.cs.kieler.klighd.lsp.model.LayoutOptionUIData import de.cau.cs.kieler.klighd.lsp.model.PerformActionAction @@ -44,6 +45,7 @@ import de.cau.cs.kieler.klighd.lsp.model.StoreImagesAction import de.cau.cs.kieler.klighd.lsp.model.UpdateDiagramOptionsAction import de.cau.cs.kieler.klighd.lsp.model.ValuedSynthesisOption import de.cau.cs.kieler.klighd.lsp.utils.KRenderingIdGenerator +import de.cau.cs.kieler.klighd.util.ColorPreferences import java.io.FileNotFoundException import java.io.InputStream import java.util.ArrayList @@ -245,6 +247,8 @@ class KGraphDiagramServer extends LanguageAwareDiagramServer { handle(action as SetSynthesisAction) } else if (action.getKind === CheckedImagesAction.KIND) { handle(action as CheckedImagesAction) + } else if (action.getKind === ClientColorPreferencesAction.KIND) { + handle(action as ClientColorPreferencesAction) } else if (action.getKind === RefreshDiagramAction.KIND) { handle(action as RefreshDiagramAction) } else if (action.getKind === RefreshLayoutAction.KIND) { @@ -383,6 +387,16 @@ class KGraphDiagramServer extends LanguageAwareDiagramServer { if (!request.requestId.nullOrEmpty) LOG.warn("Model requests are not supported by the Xtext diagram server.") copyOptions(request) + synchronized (diagramState) { + // In the request model action there may be some further information for the client color preferences to + // be applied here. + val foregroundColor = LSPUtil.parseColor(request.options.get("clientColorPreferenceForeground")) + val backgroundColor = LSPUtil.parseColor(request.options.get("clientColorPreferenceBackground")) + val highlightColor = LSPUtil.parseColor(request.options.get("clientColorPreferenceHighlight")) + if (foregroundColor !== null && backgroundColor !== null && highlightColor !== null) { + diagramState.colorPreferences = new ColorPreferences(foregroundColor, backgroundColor, highlightColor) + } + } diagramLanguageServer.diagramUpdater.updateDiagram(this) } else { super.handle(request) @@ -508,6 +522,22 @@ class KGraphDiagramServer extends LanguageAwareDiagramServer { setOrUpdateModel } } + + /** + * Called when a {@link ClientColorPreferencesAction} is received. + * Tells the server that the client has new color preferences available that should be considered. + */ + protected def handle(ClientColorPreferencesAction action) { + val foregroundColor = LSPUtil.parseColor(action.clientColorPreferences.foreground) + val backgroundColor = LSPUtil.parseColor(action.clientColorPreferences.background) + val highlightColor = LSPUtil.parseColor(action.clientColorPreferences.highlight) + + val colorPreferences = new ColorPreferences(foregroundColor, backgroundColor, highlightColor) + synchronized (diagramState) { + diagramState.colorPreferences = colorPreferences + } + updateDiagram() + } /** * Called when a {@link RefreshDiagramAction} is received. diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend index 1ad5b9fad..e5d3acdec 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend @@ -101,23 +101,28 @@ class LSPUtil { val highlightColor = parseColor(highlight) return new ColorPreferences(foregroundColor, backgroundColor, highlightColor) - } /** * Parses a single color string in the form #RRGGBB into a KColor, or null if the string is unparsable. */ - static def KColor parseColor(JsonElement jsonColor) { - if (!jsonColor.isJsonPrimitive) return null + static def KColor parseColor(String stringColor) { var KColor color = null try { - val awtColor = Color.decode(jsonColor.asString) + val awtColor = Color.decode(stringColor) color = KRenderingFactory.eINSTANCE.createKColor color.red = awtColor.red color.green = awtColor.green color.blue = awtColor.blue } catch (NumberFormatException e) {} - return color } + + /** + * Parses a single color string in the form #RRGGBB into a KColor, or null if the string is unparsable. + */ + static def KColor parseColor(JsonElement jsonColor) { + if (!jsonColor.isJsonPrimitive) return null + return parseColor(jsonColor.asString) + } } diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/gson_utils/KGraphTypeAdapterUtil.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/gson_utils/KGraphTypeAdapterUtil.xtend index d866d90ee..3a25cfd7a 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/gson_utils/KGraphTypeAdapterUtil.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/gson_utils/KGraphTypeAdapterUtil.xtend @@ -28,6 +28,7 @@ import de.cau.cs.kieler.klighd.lsp.interactive.rectpacking.RectpackingDeletePosi import de.cau.cs.kieler.klighd.lsp.interactive.rectpacking.RectpackingSetPositionConstraintAction import de.cau.cs.kieler.klighd.lsp.interactive.rectpacking.SetAspectRatioAction import de.cau.cs.kieler.klighd.lsp.model.CheckedImagesAction +import de.cau.cs.kieler.klighd.lsp.model.ClientColorPreferencesAction import de.cau.cs.kieler.klighd.lsp.model.PerformActionAction import de.cau.cs.kieler.klighd.lsp.model.RefreshDiagramAction import de.cau.cs.kieler.klighd.lsp.model.RefreshLayoutAction @@ -53,6 +54,7 @@ class KGraphTypeAdapterUtil { addActionKind(SetSynthesisAction.KIND, SetSynthesisAction) addActionKind(RefreshDiagramAction.KIND, RefreshDiagramAction) addActionKind(RefreshLayoutAction.KIND, RefreshLayoutAction) + addActionKind(ClientColorPreferencesAction.KIND, ClientColorPreferencesAction) // Interactive layered actions addActionKind(SetStaticConstraintAction.KIND, SetStaticConstraintAction) diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/model/Actions.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/model/Actions.xtend index 75aa467b6..e1fe4c333 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/model/Actions.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/model/Actions.xtend @@ -160,6 +160,36 @@ class CheckedImagesAction implements ResponseAction { } } +/** + * Sent from the client to the server to notify it about new color preferences. + * + * @author nre + */ +@Accessors +@EqualsHashCode +@ToString(skipNulls = true) +class ClientColorPreferencesAction implements Action { + public static val KIND = 'changeClientColorPreferences' + String kind = KIND + + ClientColorPreferences clientColorPreferences + + new() {} + new(Consumer initializer) { + initializer.accept(this) + } +} + +/** + * The client color preferences for individual styling for syntheses for the ClientColorPreferencesAction + */ +@Accessors +class ClientColorPreferences { + String foreground + String background + String highlight +} + /** * Sent from the client to the server to request a new diagram with the given synthesis. * From 8e12e8ec6ae0a34c6a22b2ccd636ff6683d37424 Mon Sep 17 00:00:00 2001 From: Niklas Rentz Date: Tue, 17 Oct 2023 20:22:32 +0200 Subject: [PATCH 04/10] klighd.lsp: some null checks for dark theming --- .../de/cau/cs/kieler/klighd/lsp/KGraphDiagramUpdater.xtend | 4 +++- .../src/de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramUpdater.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramUpdater.xtend index ef3e1568d..b39d7d308 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramUpdater.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramUpdater.xtend @@ -259,7 +259,9 @@ class KGraphDiagramUpdater extends DiagramUpdater { } synchronized (diagramState) { - viewContext.setProperty(KlighdProperties.COLOR_PREFERENCES, diagramState.colorPreferences) + if (diagramState.colorPreferences !== null) { + viewContext.setProperty(KlighdProperties.COLOR_PREFERENCES, diagramState.colorPreferences) + } } val vc = viewContext diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend index e5d3acdec..7dc588b49 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend @@ -92,7 +92,7 @@ class LSPUtil { * Parses a jsonElement for color preferences from the client. unreadable colors are defaulted to black and white. */ static def ColorPreferences parseColorPreferences(JsonElement jsonColors) { - if (!jsonColors.isJsonObject) return null + if (jsonColors === null || !jsonColors.isJsonObject) return null val foreground = jsonColors.asJsonObject.get("foreground") val background = jsonColors.asJsonObject.get("background") val highlight = jsonColors.asJsonObject.get("highlight") From 384124accc2d5f4733c214aab501b609010deb11 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Tue, 17 Oct 2023 21:13:54 +0200 Subject: [PATCH 05/10] Added detection of dark theme and fake dark mode for klighd canvas --- .../klighd/piccolo/viewer/PiccoloViewer.java | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/plugins/de.cau.cs.kieler.klighd.piccolo/src/de/cau/cs/kieler/klighd/piccolo/viewer/PiccoloViewer.java b/plugins/de.cau.cs.kieler.klighd.piccolo/src/de/cau/cs/kieler/klighd/piccolo/viewer/PiccoloViewer.java index 969bf8b56..3fb0b6203 100644 --- a/plugins/de.cau.cs.kieler.klighd.piccolo/src/de/cau/cs/kieler/klighd/piccolo/viewer/PiccoloViewer.java +++ b/plugins/de.cau.cs.kieler.klighd.piccolo/src/de/cau/cs/kieler/klighd/piccolo/viewer/PiccoloViewer.java @@ -16,6 +16,7 @@ */ package de.cau.cs.kieler.klighd.piccolo.viewer; +import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.AffineTransform; @@ -27,6 +28,8 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.elk.core.math.KVector; import org.eclipse.elk.core.options.CoreOptions; import org.eclipse.swt.SWT; @@ -46,6 +49,7 @@ import de.cau.cs.kieler.klighd.kgraph.KGraphElement; import de.cau.cs.kieler.klighd.kgraph.KNode; import de.cau.cs.kieler.klighd.kgraph.util.KGraphUtil; +import de.cau.cs.kieler.klighd.krendering.KRenderingUtil; import de.cau.cs.kieler.klighd.piccolo.KlighdPiccolo; import de.cau.cs.kieler.klighd.piccolo.internal.KlighdCanvas; import de.cau.cs.kieler.klighd.piccolo.internal.controller.DiagramController; @@ -58,6 +62,7 @@ import de.cau.cs.kieler.klighd.piccolo.internal.events.KlighdSelectiveZoomEventHandler; import de.cau.cs.kieler.klighd.piccolo.internal.nodes.KlighdMainCamera; import de.cau.cs.kieler.klighd.piccolo.internal.util.NodeUtil; +import de.cau.cs.kieler.klighd.util.ColorPreferences; import de.cau.cs.kieler.klighd.util.KlighdProperties; import de.cau.cs.kieler.klighd.viewers.AbstractViewer; import de.cau.cs.kieler.klighd.viewers.ContextViewer; @@ -140,8 +145,28 @@ public PiccoloViewer(final ContextViewer theParentViewer, final Composite parent throw new IllegalArgumentException(msg); } this.parentViewer = theParentViewer; - this.canvas = new KlighdCanvas(parent, style, - theParentViewer.getViewContext().getProperty(KlighdProperties.CANVAS_COLOR)); + + ViewContext viewContext = theParentViewer.getViewContext(); + Color background = viewContext.getProperty(KlighdProperties.CANVAS_COLOR); + if (!viewContext.hasProperty(KlighdProperties.CANVAS_COLOR)) { + // Check and activate dark theme + IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode("org.eclipse.e4.ui.css.swt.theme"); + if (prefs != null) { + if ("org.eclipse.e4.ui.css.theme.e4_dark".equals(prefs.get("themeid", ""))) { + // Simulate dark mode + background = Color.decode("#2f2f2f"); + ColorPreferences colors = new ColorPreferences( + KRenderingUtil.getColor("#cccccc"), + KRenderingUtil.getColor("#2f2f2f"), + KRenderingUtil.getColor("#cccccc") + ); + viewContext.setProperty(KlighdProperties.COLOR_PREFERENCES, colors); + } else { + viewContext.setProperty(KlighdProperties.COLOR_PREFERENCES, KlighdProperties.COLOR_PREFERENCES.getDefault()); + } + } + } + this.canvas = new KlighdCanvas(parent, style, background); final KlighdMainCamera camera = canvas.getCamera(); this.magnificationLensVisibleSupplier = installEventHanders(camera); From b7afff9d551fdbb5900bfe0517e40631bcfa10ae Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 18 Oct 2023 11:26:27 +0200 Subject: [PATCH 06/10] Switched to null as default for ColorPreferences property --- .../cs/kieler/klighd/util/ColorPreferences.java | 15 +-------------- .../cs/kieler/klighd/util/KlighdProperties.java | 3 ++- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorPreferences.java b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorPreferences.java index 8608a7333..32545869f 100644 --- a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorPreferences.java +++ b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorPreferences.java @@ -46,20 +46,7 @@ public class ColorPreferences { * Default constructor for color preferences, setting foreground and highlight to black, background to white. */ public ColorPreferences() { - foreground = KRenderingFactory.eINSTANCE.createKColor(); - foreground.setRed(0); - foreground.setGreen(0); - foreground.setBlue(0); - - background = KRenderingFactory.eINSTANCE.createKColor(); - background.setRed(255); - background.setGreen(255); - background.setBlue(255); - - highlight = KRenderingFactory.eINSTANCE.createKColor(); - highlight.setRed(0); - highlight.setGreen(0); - highlight.setBlue(0); + this(null, null, null); } /** diff --git a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/KlighdProperties.java b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/KlighdProperties.java index 614f8d969..a79002db5 100644 --- a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/KlighdProperties.java +++ b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/KlighdProperties.java @@ -383,9 +383,10 @@ public static boolean isSelectable(final EObject viewElement) { /** * Property determining the custom color theme to be used by syntheses. + * Null means no preferences could be detected and the diagram background will most likely be white. */ public static final IProperty COLOR_PREFERENCES = - new Property("klighd.colorPreferences", new ColorPreferences()); + new Property("klighd.colorPreferences", null); /** * Property determining whether this node should be rendered as a proxy. From 5efe94c19b7a7ab3dc6fe9ccb3888a8b35862fa6 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Mon, 23 Oct 2023 16:05:17 +0200 Subject: [PATCH 07/10] Added dark themes for default syntheses --- .../syntheses/EObjectFallbackSynthesis.xtend | 114 ++++++++++++++---- .../ide/syntheses/ErrorModelSynthesis.xtend | 5 +- .../ide/syntheses/MessageModelSynthesis.xtend | 20 +++ 3 files changed, 111 insertions(+), 28 deletions(-) diff --git a/plugins/de.cau.cs.kieler.klighd.ide/src/de/cau/cs/kieler/klighd/ide/syntheses/EObjectFallbackSynthesis.xtend b/plugins/de.cau.cs.kieler.klighd.ide/src/de/cau/cs/kieler/klighd/ide/syntheses/EObjectFallbackSynthesis.xtend index c594c448d..ffd778b4a 100644 --- a/plugins/de.cau.cs.kieler.klighd.ide/src/de/cau/cs/kieler/klighd/ide/syntheses/EObjectFallbackSynthesis.xtend +++ b/plugins/de.cau.cs.kieler.klighd.ide/src/de/cau/cs/kieler/klighd/ide/syntheses/EObjectFallbackSynthesis.xtend @@ -20,10 +20,11 @@ import com.google.common.collect.HashBasedTable import com.google.common.collect.Table import de.cau.cs.kieler.klighd.KlighdConstants import de.cau.cs.kieler.klighd.SynthesisOption +import de.cau.cs.kieler.klighd.ide.syntheses.action.EcoreModelExpandDetailsAction import de.cau.cs.kieler.klighd.kgraph.KNode import de.cau.cs.kieler.klighd.kgraph.KPort import de.cau.cs.kieler.klighd.kgraph.util.KGraphUtil -import de.cau.cs.kieler.klighd.krendering.Colors +import de.cau.cs.kieler.klighd.krendering.KColor import de.cau.cs.kieler.klighd.krendering.LineStyle import de.cau.cs.kieler.klighd.krendering.extensions.KColorExtensions import de.cau.cs.kieler.klighd.krendering.extensions.KContainerRenderingExtensions @@ -35,8 +36,8 @@ import de.cau.cs.kieler.klighd.krendering.extensions.KPortExtensions import de.cau.cs.kieler.klighd.krendering.extensions.KRenderingExtensions import de.cau.cs.kieler.klighd.microlayout.PlacementUtil import de.cau.cs.kieler.klighd.syntheses.AbstractDiagramSynthesis -import de.cau.cs.kieler.klighd.ide.syntheses.action.EcoreModelExpandDetailsAction import de.cau.cs.kieler.klighd.util.KlighdProperties +import java.awt.Color import java.util.List import java.util.Map import javax.inject.Inject @@ -55,6 +56,7 @@ import org.eclipse.emf.ecore.EStructuralFeature import org.eclipse.emf.ecore.util.EContentsEList import static extension de.cau.cs.kieler.klighd.syntheses.DiagramSyntheses.* +import static extension org.eclipse.emf.ecore.util.EcoreUtil.copy /** * Diagram synthesis of a {@link EObject}. @@ -116,11 +118,20 @@ class EObjectFallbackSynthesis extends AbstractDiagramSynthesis { val Map nodeCache = newHashMap val Table portCache = HashBasedTable.create + // Colors + var KColor foregroundColor + var KColor backgroundColor + var KColor foregroundNodeColor + var KColor backgroundNodeColor + override KNode transform(EObject model) { // Init cache nodeCache.clear portCache.clear + // establish color theme + configureColors() + val rootNode = createNode(); rootNode.addLayoutParam(CoreOptions::ALGORITHM, LayeredOptions.ALGORITHM_ID); @@ -149,8 +160,12 @@ class EObjectFallbackSynthesis extends AbstractDiagramSynthesis { } it.target = nodeCache.get(eObject); it.addPolyline => [ - addHeadArrowDecorator - addJunctionPointDecorator + it.foreground = foregroundNodeColor.copy; + it.addHeadArrowDecorator + it.addJunctionPointDecorator => [ + it.foreground = foregroundNodeColor.copy + it.background = foregroundNodeColor.copy + ] ] ] } @@ -169,9 +184,13 @@ class EObjectFallbackSynthesis extends AbstractDiagramSynthesis { } it.target = nodeCache.get(targetEObject); it.addPolyline => [ - addHeadArrowDecorator - addJunctionPointDecorator - lineStyle = LineStyle.DASH + it.foreground = foregroundNodeColor.copy; + it.addHeadArrowDecorator + it.addJunctionPointDecorator => [ + it.foreground = foregroundNodeColor.copy + it.background = foregroundNodeColor.copy + ] + it.lineStyle = LineStyle.DASH ]; ] } @@ -197,14 +216,15 @@ class EObjectFallbackSynthesis extends AbstractDiagramSynthesis { // Expanded Rectangle node.createFigure() => [ it.setProperty(KlighdProperties::EXPANDED_RENDERING, true); + it.addDoubleClickAction(KlighdConstants::ACTION_COLLAPSE_EXPAND); it.setGridPlacement(1); // add name of object as text it.addText(object.eClass.name).associateWith(object) => [ it.fontSize = 11; - it.setFontBold(true); + it.foreground = foregroundColor.copy; it.setGridPlacementData().from(LEFT, 9, 0, TOP, 8f, 0).to(RIGHT, 8, 0, BOTTOM, 4, 0); - it.suppressSelectability; + it.selectionFontBold = false; ]; // collapse button @@ -214,14 +234,16 @@ class EObjectFallbackSynthesis extends AbstractDiagramSynthesis { it.addSingleClickAction(KlighdConstants::ACTION_COLLAPSE_EXPAND); it.addDoubleClickAction(KlighdConstants::ACTION_COLLAPSE_EXPAND); it.setGridPlacementData().from(LEFT, 8, 0, TOP, 0, 0).to(RIGHT, 8, 0, BOTTOM, 0, 0); + it.selectionFontBold = false; + it.suppressSelectability; ]; if (hasAttributes || hasSuperTypes) { // Add Details it.addRectangle => [ it.setGridPlacementData.from(LEFT, 8, 0, TOP, 0, 0).to(RIGHT, 8, 0, BOTTOM, 8, 0); - it.setBackground = "white".color; - it.foreground = "gray".color + it.background = backgroundColor.copy; + it.foreground = foregroundNodeColor.copy; it.lineWidth = 1; it.setGridPlacement(1); @@ -230,16 +252,19 @@ class EObjectFallbackSynthesis extends AbstractDiagramSynthesis { // add all super types as string representation if (hasSuperTypes) { it.addText("SuperTypes") => [ - it.foreground = "gray".color; + it.foreground = foregroundNodeColor.copy; it.fontSize = 8; it.setGridPlacementData().from(LEFT, 5, 0, TOP, 2, 0).to(RIGHT, 5, 0, BOTTOM, 2, 0); + it.selectionFontBold = false; it.suppressSelectability; ]; eclass.EAllSuperTypes.filterNull.forEach [ // add a text with name and value of the attribute container.addText(it.name) => [ it.fontSize = 9; + it.foreground = foregroundColor.copy; it.setGridPlacementData().from(LEFT, 5, 0, TOP, 2, 0).to(RIGHT, 5, 0, BOTTOM, 2, 0); + it.selectionFontBold = false; it.suppressSelectability; ]; ] @@ -247,19 +272,22 @@ class EObjectFallbackSynthesis extends AbstractDiagramSynthesis { // add all attributes as string representation if (hasAttributes) { if (hasSuperTypes) { - it.addHorizontalSeperatorLine(1, 1).foreground = "gray".color; + it.addHorizontalSeperatorLine(1, 1).foreground = foregroundNodeColor.copy; } it.addText("Attributes") => [ - it.foreground = "gray".color; + it.foreground = foregroundNodeColor.copy; it.fontSize = 8; it.setGridPlacementData().from(LEFT, 5, 0, TOP, 2, 0).to(RIGHT, 5, 0, BOTTOM, 2, 0); + it.selectionFontBold = false; it.suppressSelectability; ]; eclass.EAllAttributes.filterNull.forEach [ // add a text with name and value of the attribute container.addText(it.name + ": " + String::valueOf(object.eGet(it))) => [ it.fontSize = 9; + it.foreground = foregroundColor.copy; it.setGridPlacementData().from(LEFT, 5, 0, TOP, 2, 0).to(RIGHT, 5, 0, BOTTOM, 2, 0); + it.selectionFontBold = false; it.suppressSelectability; ]; ] @@ -269,27 +297,30 @@ class EObjectFallbackSynthesis extends AbstractDiagramSynthesis { ]; // Collapse Rectangle - node.createFigure() => - [ + node.createFigure() => [ it.setProperty(KlighdProperties::COLLAPSED_RENDERING, true); + it.addDoubleClickAction(KlighdConstants::ACTION_COLLAPSE_EXPAND); it.setGridPlacement(1); + // add name of object as text - it.addText(object.eClass.name).associateWith(object) => - [ + it.addText(object.eClass.name).associateWith(object) => [ it.fontSize = 11; - it.setFontBold(true); + it.foreground = foregroundColor.copy; it.setGridPlacementData().from(LEFT, 8, 0, TOP, 8, 0).to(RIGHT, 8, 0, BOTTOM, if(hasAttributes || hasSuperTypes) 4 else 8, 0); - it.suppressSelectability; + it.selectionFontBold = false; ]; + if (hasAttributes || hasSuperTypes) { // Add details button it.addText("[Details]") => [ - it.foreground = "blue".color - it.fontSize = 9 + it.foreground = "blue".color; + it.fontSize = 9; it.addSingleClickAction(KlighdConstants::ACTION_COLLAPSE_EXPAND); it.addDoubleClickAction(KlighdConstants::ACTION_COLLAPSE_EXPAND); it.setGridPlacementData().from(LEFT, 8, 0, TOP, 0, 0).to(RIGHT, 8, 0, BOTTOM, 8, 0); + it.selectionFontBold = false; + it.suppressSelectability; ]; } ]; @@ -306,14 +337,16 @@ class EObjectFallbackSynthesis extends AbstractDiagramSynthesis { private def transformToPort(EStructuralFeature containerFeature, KNode node) { val port = KGraphUtil::createInitializedPort node.ports += port; - port.setPortSize(portEdgeLength, portEdgeLength); + port.setPortSize(0, 0); port.addLayoutParam(CoreOptions::PORT_SIDE, PortSide::EAST); port.setPortPos(node.width-1, node.nextEPortYPosition); port.createLabel => [ - text = containerFeature.name + it.text = containerFeature.name val size = PlacementUtil.estimateSize(it) it.width = size.width it.height = size.height + it.configureOutsidePortLabel(containerFeature.name) + it.getKRendering.foreground = foregroundColor.copy ] // Add to cache @@ -328,12 +361,41 @@ class EObjectFallbackSynthesis extends AbstractDiagramSynthesis { private def createFigure(KNode node) { val figure = node.addRoundedRectangle(8, 8, 1); figure.lineWidth = 1; - figure.foreground = Colors.GRAY; - figure.background = Colors.GRAY_95; + figure.foreground = foregroundNodeColor.copy; + figure.background = backgroundNodeColor.copy; // minimal node size is necessary if no text will be added node.setMinimalNodeSize(2 * figure.cornerWidth, 2 * figure.cornerHeight); return figure; } + + /** + * Configure the colors used in the diagram + */ + def configureColors() { + // default colors + foregroundColor = "black".color + backgroundColor = "white".color + foregroundNodeColor = "#bebebe".color + backgroundNodeColor = "#f2f2f2".color + + val colorPrefereces = usedContext.getProperty(KlighdProperties.COLOR_PREFERENCES); + if (colorPrefereces !== null) { + foregroundColor = colorPrefereces.foreground; + backgroundColor = colorPrefereces.background; + + val fgHSB = Color.RGBtoHSB(foregroundColor.getRed(), foregroundColor.getGreen(), foregroundColor.getBlue(), null); + val bgHSB = Color.RGBtoHSB(backgroundColor.getRed(), backgroundColor.getGreen(), backgroundColor.getBlue(), null); + val adjustment = bgHSB.get(2) < 0.6f ? -0.1f : 0.04f; // reduce brightness if color is dark dark + + fgHSB.set(2, Math.max(0, Math.min(1, fgHSB.get(2) + adjustment))); + val newFg = Color.getHSBColor(fgHSB.get(0), fgHSB.get(1), fgHSB.get(2)); + foregroundNodeColor.setColor(newFg.red, newFg.green, newFg.blue); + + bgHSB.set(2, Math.max(0, Math.min(1, bgHSB.get(2) - adjustment))); + val newBg = Color.getHSBColor(bgHSB.get(0), bgHSB.get(1), bgHSB.get(2)); + backgroundNodeColor.setColor(newBg.red, newBg.green, newBg.blue); + } + } } diff --git a/plugins/de.cau.cs.kieler.klighd.ide/src/de/cau/cs/kieler/klighd/ide/syntheses/ErrorModelSynthesis.xtend b/plugins/de.cau.cs.kieler.klighd.ide/src/de/cau/cs/kieler/klighd/ide/syntheses/ErrorModelSynthesis.xtend index 61fd73754..9f97b4bbd 100644 --- a/plugins/de.cau.cs.kieler.klighd.ide/src/de/cau/cs/kieler/klighd/ide/syntheses/ErrorModelSynthesis.xtend +++ b/plugins/de.cau.cs.kieler.klighd.ide/src/de/cau/cs/kieler/klighd/ide/syntheses/ErrorModelSynthesis.xtend @@ -43,7 +43,7 @@ class ErrorModelSynthesis extends AbstractDiagramSynthesis { extension KContainerRenderingExtensions @Inject - extension MessageModelSynthesis + MessageModelSynthesis delegate // ------------------------------------------------------------------------- // Constants @@ -53,7 +53,8 @@ class ErrorModelSynthesis extends AbstractDiagramSynthesis { // Synthesis override KNode transform(ErrorModel model) { // create basic representation with super synthesis - val rootNode = (model as MessageModel).transform; + delegate.use(usedContext); + val rootNode = delegate.transform(model as MessageModel); // Adjust diagram if (rootNode !== null && !rootNode.children.empty) { val messageNode = rootNode.children.head; diff --git a/plugins/de.cau.cs.kieler.klighd.ide/src/de/cau/cs/kieler/klighd/ide/syntheses/MessageModelSynthesis.xtend b/plugins/de.cau.cs.kieler.klighd.ide/src/de/cau/cs/kieler/klighd/ide/syntheses/MessageModelSynthesis.xtend index f5af0a26c..9c875f7fc 100644 --- a/plugins/de.cau.cs.kieler.klighd.ide/src/de/cau/cs/kieler/klighd/ide/syntheses/MessageModelSynthesis.xtend +++ b/plugins/de.cau.cs.kieler.klighd.ide/src/de/cau/cs/kieler/klighd/ide/syntheses/MessageModelSynthesis.xtend @@ -18,13 +18,16 @@ package de.cau.cs.kieler.klighd.ide.syntheses import de.cau.cs.kieler.klighd.ide.model.MessageModel import de.cau.cs.kieler.klighd.kgraph.KNode +import de.cau.cs.kieler.klighd.krendering.extensions.KColorExtensions import de.cau.cs.kieler.klighd.krendering.extensions.KContainerRenderingExtensions import de.cau.cs.kieler.klighd.krendering.extensions.KNodeExtensions import de.cau.cs.kieler.klighd.krendering.extensions.KRenderingExtensions import de.cau.cs.kieler.klighd.syntheses.AbstractDiagramSynthesis +import de.cau.cs.kieler.klighd.util.KlighdProperties import javax.inject.Inject import static extension de.cau.cs.kieler.klighd.syntheses.DiagramSyntheses.* +import static extension org.eclipse.emf.ecore.util.EcoreUtil.copy /** * Diagram synthesis for a {@link MessageModel}. @@ -42,6 +45,9 @@ class MessageModelSynthesis extends AbstractDiagramSynthesis { @Inject extension KContainerRenderingExtensions + @Inject + extension KColorExtensions + // ------------------------------------------------------------------------- // Constants public static val String ID = "de.cau.cs.kieler.klighd.ui.view.syntheses.MessageModelSynthesis"; @@ -50,6 +56,15 @@ class MessageModelSynthesis extends AbstractDiagramSynthesis { // Synthesis override KNode transform(MessageModel model) { val rootNode = createNode(); + + // color theme + val colorPrefereces = usedContext.getProperty(KlighdProperties.COLOR_PREFERENCES); + val fg = if (colorPrefereces !== null) { + colorPrefereces.foreground; + } else { + "#000000".color; + } + rootNode.children += createNode(model) => [ it.addRectangle() => [ it.invisible = true; @@ -68,12 +83,15 @@ class MessageModelSynthesis extends AbstractDiagramSynthesis { it.addRoundedRectangle(7, 7) => [ it.setGridPlacement(1); it.lineWidth = 2; + it.foreground = fg.copy; //title if (model.getTitle !== null) { it.addText(model.getTitle) => [ it.fontSize = 12; it.setFontBold = true; + it.foreground = fg.copy; it.setGridPlacementData().from(LEFT, 8, 0, TOP, 8, 0).to(RIGHT, 8, 0, BOTTOM, 4, 0); + it.selectionFontBold = false; // No selection style it.suppressSelectability; ] } @@ -81,11 +99,13 @@ class MessageModelSynthesis extends AbstractDiagramSynthesis { if (model.getMessage !== null) { it.addText(model.getMessage) => [ it.fontSize = 12; + it.foreground = fg.copy; if (model.getTitle !== null) { it.setGridPlacementData().from(LEFT, 8, 0, TOP, 0, 0).to(RIGHT, 8, 0, BOTTOM, 4, 0); } else { it.setGridPlacementData().from(LEFT, 8, 0, TOP, 8, 0).to(RIGHT, 8, 0, BOTTOM, 8, 0); } + it.selectionFontBold = false; // No selection style it.suppressSelectability; ] } From 7a678b7f5b113974116465a18d9b6639489ab8b0 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Mon, 23 Oct 2023 17:19:45 +0200 Subject: [PATCH 08/10] Added support for color preferences when using nested syntheses --- .../src/de/cau/cs/kieler/klighd/ViewContext.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/ViewContext.java b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/ViewContext.java index 5325f9a48..892c4fa78 100644 --- a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/ViewContext.java +++ b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/ViewContext.java @@ -142,6 +142,11 @@ public ViewContext(final ViewContext otherContext, final Object inputModel) { this.businessModel = inputModel; if (otherContext != null) { this.synthesisOptionConfig.putAll(otherContext.synthesisOptionConfig); + // Color preferences are kind of synthesis option and should be copied + if (otherContext.hasProperty(KlighdProperties.COLOR_PREFERENCES)) { + this.setProperty(KlighdProperties.COLOR_PREFERENCES, + otherContext.getProperty(KlighdProperties.COLOR_PREFERENCES)); + } } } From 057645afde1d5a5a727ea214beeb718a6317ea68 Mon Sep 17 00:00:00 2001 From: Niklas Rentz Date: Wed, 18 Dec 2024 15:34:58 +0100 Subject: [PATCH 09/10] fixed requestModelAction handling for diagram colors --- .../cs/kieler/klighd/lsp/KGraphDiagramServer.xtend | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramServer.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramServer.xtend index 5c893a337..14a9b0ec3 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramServer.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramServer.xtend @@ -255,16 +255,11 @@ class KGraphDiagramServer extends LanguageAwareDiagramServer { } /** - * Taken from {@code DefaultDiagramServer.handle(RequestModelAction)} to use this getModel. - * Needed for KeithUpdateModelAction - * - * FIXME Remove this if UpdateModelAction has a cause. + * additionally read the color preferences from the request model action. */ override protected handle(RequestModelAction request) { + super.handle(request) if (model.type == 'NONE' && diagramLanguageServer !== null) { - if (!request.requestId.nullOrEmpty) -// LOG.warn("Model requests are not supported by the Xtext diagram server.") - copyOptions(request) synchronized (diagramState) { // In the request model action there may be some further information for the client color preferences to // be applied here. @@ -275,9 +270,6 @@ class KGraphDiagramServer extends LanguageAwareDiagramServer { diagramState.colorPreferences = new ColorPreferences(foregroundColor, backgroundColor, highlightColor) } } - diagramLanguageServer.diagramUpdater.updateDiagram(this) - } else { - super.handle(request) } } From 0ccc7a20b8344736698496d702650b64b8b88342 Mon Sep 17 00:00:00 2001 From: Niklas Rentz Date: Fri, 20 Dec 2024 14:06:37 +0100 Subject: [PATCH 10/10] Include a general theme type in the color theming. --- .gitignore | 6 +- .../syntheses/EObjectFallbackSynthesis.xtend | 1 + .../klighd/lsp/KGraphDiagramGenerator.xtend | 6 ++ .../klighd/lsp/KGraphDiagramServer.xtend | 23 +----- .../de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend | 9 ++- .../cs/kieler/klighd/lsp/model/Actions.xtend | 2 + .../klighd/piccolo/viewer/PiccoloViewer.java | 2 + .../kieler/klighd/util/ColorPreferences.java | 70 ++++++++++++++----- .../cs/kieler/klighd/util/ColorThemeKind.java | 41 +++++++++++ .../kieler/klighd/util/KlighdProperties.java | 7 ++ 10 files changed, 124 insertions(+), 43 deletions(-) create mode 100644 plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorThemeKind.java diff --git a/.gitignore b/.gitignore index 585203ce5..db78f1b8f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ bin/ xtend-gen/ target/ -localM2/ -klighd-snapshots/ -klighd/ +/localM2/ +/klighd-snapshots/ +/klighd/ .svn/ .DS_Store *.class diff --git a/plugins/de.cau.cs.kieler.klighd.ide/src/de/cau/cs/kieler/klighd/ide/syntheses/EObjectFallbackSynthesis.xtend b/plugins/de.cau.cs.kieler.klighd.ide/src/de/cau/cs/kieler/klighd/ide/syntheses/EObjectFallbackSynthesis.xtend index 43e1890ae..175c9321f 100644 --- a/plugins/de.cau.cs.kieler.klighd.ide/src/de/cau/cs/kieler/klighd/ide/syntheses/EObjectFallbackSynthesis.xtend +++ b/plugins/de.cau.cs.kieler.klighd.ide/src/de/cau/cs/kieler/klighd/ide/syntheses/EObjectFallbackSynthesis.xtend @@ -139,6 +139,7 @@ class EObjectFallbackSynthesis extends AbstractDiagramSynthesis { rootNode.setLayoutOption(LayeredOptions::NODE_PLACEMENT_BK_EDGE_STRAIGHTENING, EdgeStraighteningStrategy.IMPROVE_STRAIGHTNESS); rootNode.setLayoutOption(LayeredOptions::SPACING_EDGE_NODE, LayeredOptions.SPACING_NODE_NODE.^default * 1.1f); rootNode.setLayoutOption(LayeredOptions::SPACING_EDGE_NODE_BETWEEN_LAYERS, LayeredOptions.SPACING_NODE_NODE.^default * 1.1f); + rootNode.setProperty(KlighdProperties.DIAGRAM_BACKGROUND, backgroundColor) // transform root object rootNode.children += model.transformToNode diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramGenerator.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramGenerator.xtend index aa2ecb3c6..b62109f36 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramGenerator.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramGenerator.xtend @@ -152,6 +152,12 @@ class KGraphDiagramGenerator implements IDiagramGenerator { id = uri children = new ArrayList ] + + // Special property for the diagram background, to be set on the diagram root. + val background = parentNode.getProperty(KlighdProperties.DIAGRAM_BACKGROUND) + if (background !== null) { + (diagramRoot as SKGraph).properties.put(KlighdProperties.DIAGRAM_BACKGROUND.id, background) + } diagramRoot.children.addAll(createNodesAndPrepareEdges(#[parentNode], diagramRoot)) // Do post processing. diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramServer.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramServer.xtend index 14a9b0ec3..531c7dea8 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramServer.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramServer.xtend @@ -62,7 +62,6 @@ import org.eclipse.emf.ecore.EObject import org.eclipse.sprotty.Action import org.eclipse.sprotty.ActionMessage import org.eclipse.sprotty.RejectAction -import org.eclipse.sprotty.RequestModelAction import org.eclipse.sprotty.SModelElement import org.eclipse.sprotty.SModelRoot import org.eclipse.sprotty.SelectAction @@ -253,25 +252,6 @@ class KGraphDiagramServer extends LanguageAwareDiagramServer { notificationHandler.sendErrorAndThrow(e) } } - - /** - * additionally read the color preferences from the request model action. - */ - override protected handle(RequestModelAction request) { - super.handle(request) - if (model.type == 'NONE' && diagramLanguageServer !== null) { - synchronized (diagramState) { - // In the request model action there may be some further information for the client color preferences to - // be applied here. - val foregroundColor = LSPUtil.parseColor(request.options.get("clientColorPreferenceForeground")) - val backgroundColor = LSPUtil.parseColor(request.options.get("clientColorPreferenceBackground")) - val highlightColor = LSPUtil.parseColor(request.options.get("clientColorPreferenceHighlight")) - if (foregroundColor !== null && backgroundColor !== null && highlightColor !== null) { - diagramState.colorPreferences = new ColorPreferences(foregroundColor, backgroundColor, highlightColor) - } - } - } - } /** * Called when a {@link PerformActionAction} is received. @@ -377,11 +357,12 @@ class KGraphDiagramServer extends LanguageAwareDiagramServer { * Tells the server that the client has new color preferences available that should be considered. */ protected def handle(ClientColorPreferencesAction action) { + val kind = action.clientColorPreferences.kind val foregroundColor = LSPUtil.parseColor(action.clientColorPreferences.foreground) val backgroundColor = LSPUtil.parseColor(action.clientColorPreferences.background) val highlightColor = LSPUtil.parseColor(action.clientColorPreferences.highlight) - val colorPreferences = new ColorPreferences(foregroundColor, backgroundColor, highlightColor) + val colorPreferences = new ColorPreferences(kind, foregroundColor, backgroundColor, highlightColor) synchronized (diagramState) { diagramState.colorPreferences = colorPreferences } diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend index 7dc588b49..1c69a1649 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/LSPUtil.xtend @@ -3,7 +3,7 @@ * * http://rtsys.informatik.uni-kiel.de/kieler * - * Copyright 2020 by + * Copyright 2020-2024 by * + Kiel University * + Department of Computer Science * + Real-Time and Embedded Systems Group @@ -23,6 +23,7 @@ import de.cau.cs.kieler.klighd.kgraph.KNode import de.cau.cs.kieler.klighd.krendering.KColor import de.cau.cs.kieler.klighd.krendering.KRenderingFactory import de.cau.cs.kieler.klighd.util.ColorPreferences +import de.cau.cs.kieler.klighd.util.ColorThemeKind import java.awt.Color /** @@ -93,14 +94,16 @@ class LSPUtil { */ static def ColorPreferences parseColorPreferences(JsonElement jsonColors) { if (jsonColors === null || !jsonColors.isJsonObject) return null + val kind = jsonColors.asJsonObject.get("kind") val foreground = jsonColors.asJsonObject.get("foreground") val background = jsonColors.asJsonObject.get("background") val highlight = jsonColors.asJsonObject.get("highlight") + val ColorThemeKind colorKind = if (kind === null) ColorThemeKind.LIGHT else ColorThemeKind.values.get(kind.asInt) val foregroundColor = parseColor(foreground) val backgroundColor = parseColor(background) val highlightColor = parseColor(highlight) - return new ColorPreferences(foregroundColor, backgroundColor, highlightColor) + return new ColorPreferences(colorKind, foregroundColor, backgroundColor, highlightColor) } /** @@ -122,7 +125,7 @@ class LSPUtil { * Parses a single color string in the form #RRGGBB into a KColor, or null if the string is unparsable. */ static def KColor parseColor(JsonElement jsonColor) { - if (!jsonColor.isJsonPrimitive) return null + if (jsonColor === null || !jsonColor.isJsonPrimitive) return null return parseColor(jsonColor.asString) } } diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/model/Actions.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/model/Actions.xtend index cc41a9a79..6a045734b 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/model/Actions.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/model/Actions.xtend @@ -17,6 +17,7 @@ package de.cau.cs.kieler.klighd.lsp.model import de.cau.cs.kieler.klighd.krendering.KImage +import de.cau.cs.kieler.klighd.util.ColorThemeKind import java.util.List import java.util.Map import java.util.Set @@ -184,6 +185,7 @@ class ClientColorPreferencesAction implements Action { */ @Accessors class ClientColorPreferences { + ColorThemeKind kind String foreground String background String highlight diff --git a/plugins/de.cau.cs.kieler.klighd.piccolo/src/de/cau/cs/kieler/klighd/piccolo/viewer/PiccoloViewer.java b/plugins/de.cau.cs.kieler.klighd.piccolo/src/de/cau/cs/kieler/klighd/piccolo/viewer/PiccoloViewer.java index 3fb0b6203..2535f2f4a 100644 --- a/plugins/de.cau.cs.kieler.klighd.piccolo/src/de/cau/cs/kieler/klighd/piccolo/viewer/PiccoloViewer.java +++ b/plugins/de.cau.cs.kieler.klighd.piccolo/src/de/cau/cs/kieler/klighd/piccolo/viewer/PiccoloViewer.java @@ -63,6 +63,7 @@ import de.cau.cs.kieler.klighd.piccolo.internal.nodes.KlighdMainCamera; import de.cau.cs.kieler.klighd.piccolo.internal.util.NodeUtil; import de.cau.cs.kieler.klighd.util.ColorPreferences; +import de.cau.cs.kieler.klighd.util.ColorThemeKind; import de.cau.cs.kieler.klighd.util.KlighdProperties; import de.cau.cs.kieler.klighd.viewers.AbstractViewer; import de.cau.cs.kieler.klighd.viewers.ContextViewer; @@ -156,6 +157,7 @@ public PiccoloViewer(final ContextViewer theParentViewer, final Composite parent // Simulate dark mode background = Color.decode("#2f2f2f"); ColorPreferences colors = new ColorPreferences( + ColorThemeKind.DARK, KRenderingUtil.getColor("#cccccc"), KRenderingUtil.getColor("#2f2f2f"), KRenderingUtil.getColor("#cccccc") diff --git a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorPreferences.java b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorPreferences.java index 32545869f..edaca03c0 100644 --- a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorPreferences.java +++ b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorPreferences.java @@ -3,7 +3,7 @@ * * http://rtsys.informatik.uni-kiel.de/kieler * - * Copyright 2023 by + * Copyright 2023-2024 by * + Kiel University * + Department of Computer Science * + Real-Time and Embedded Systems Group @@ -21,12 +21,16 @@ /** * Color preferences for KLighD syntheses. May provide information about the foreground, background, and a highlighting - * color, that may be set by a frontend. + * color, that may be set by a frontend. Alternatively, this preference sets a general color theme kind instead of + * pre-defined colors. * * @author nre */ public class ColorPreferences { +// The kind of color theme to let the synthesis pick its own color palette. + private ColorThemeKind kind; + /** * The foreground color, to be used e.g. for texts, lines, etc., or as a reference. */ @@ -46,42 +50,76 @@ public class ColorPreferences { * Default constructor for color preferences, setting foreground and highlight to black, background to white. */ public ColorPreferences() { - this(null, null, null); + this(null, null, null, null); } /** * Creates a new ColorPreferences with the given colors, or the default colors if some parameters are {@code null}. * + * @param kind * @param foreground * @param background * @param highlight */ - public ColorPreferences(KColor foreground, KColor background, KColor highlight) { + public ColorPreferences(ColorThemeKind kind, KColor foreground, KColor background, KColor highlight) { + this.kind = kind; + if (this.kind == null) { + this.kind = ColorThemeKind.LIGHT; + } if (foreground == null) { - foreground = KRenderingFactory.eINSTANCE.createKColor(); - foreground.setRed(0); - foreground.setGreen(0); - foreground.setBlue(0); + this.foreground = KRenderingFactory.eINSTANCE.createKColor(); + if (this.kind == ColorThemeKind.LIGHT || this.kind == ColorThemeKind.HIGH_CONTRAST_LIGHT) { + this.foreground.setRed(0); + this.foreground.setGreen(0); + this.foreground.setBlue(0); + } else { // dark theme + // D4D4D4 + this.foreground.setRed(212); + this.foreground.setGreen(212); + this.foreground.setBlue(212); + } } else { this.foreground = foreground; } if (background == null) { - background = KRenderingFactory.eINSTANCE.createKColor(); - background.setRed(255); - background.setGreen(255); - background.setBlue(255); + this.background = KRenderingFactory.eINSTANCE.createKColor(); + if (this.kind == ColorThemeKind.LIGHT || this.kind == ColorThemeKind.HIGH_CONTRAST_LIGHT) { + this.background.setRed(255); + this.background.setGreen(255); + this.background.setBlue(255); + } else { // dark theme + // 1E1E1E + this.background.setRed(30); + this.background.setGreen(30); + this.background.setBlue(30); + } } else { this.background = background; } if (highlight == null) { - highlight = KRenderingFactory.eINSTANCE.createKColor(); - highlight.setRed(0); - highlight.setGreen(0); - highlight.setBlue(0); + this.highlight = KRenderingFactory.eINSTANCE.createKColor(); + if (this.kind == ColorThemeKind.LIGHT || this.kind == ColorThemeKind.HIGH_CONTRAST_LIGHT) { + // 005FB8 + this.highlight.setRed(0); + this.highlight.setGreen(95); + this.highlight.setBlue(184); + } else { // dark theme + // 0078D4 + this.highlight.setRed(0); + this.highlight.setGreen(120); + this.highlight.setBlue(212); + } } else { this.highlight = highlight; } } + + /** + * @return the theme kind + */ + public ColorThemeKind getKind() { + return kind; + } /** * @return the foreground color diff --git a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorThemeKind.java b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorThemeKind.java new file mode 100644 index 000000000..cac59a21c --- /dev/null +++ b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/ColorThemeKind.java @@ -0,0 +1,41 @@ +/* + * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient + * + * http://rtsys.informatik.uni-kiel.de/kieler + * + * Copyright 2024 by + * + Kiel University + * + Department of Computer Science + * + Real-Time and Embedded Systems Group + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + */ +package de.cau.cs.kieler.klighd.util; + +/** + * Kinds of color themes, as an enum similar to VS Code's ColorThemeKind. + * + * @author nre + */ +public enum ColorThemeKind { + /** + * Light color theme with light backgrounds and darker writing + */ + LIGHT, + /** + * Dark color theme with dark backgrounds and lighter writing + */ + DARK, + /** + * Light color theme with a higher contrast. + */ + HIGH_CONTRAST_LIGHT, + /** + * Dark color theme with a higher contrast. + */ + HIGH_CONTRAST_DARK +} diff --git a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/KlighdProperties.java b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/KlighdProperties.java index a79002db5..9eb5d795e 100644 --- a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/KlighdProperties.java +++ b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/KlighdProperties.java @@ -33,6 +33,7 @@ import de.cau.cs.kieler.klighd.kgraph.KGraphData; import de.cau.cs.kieler.klighd.kgraph.KGraphElement; import de.cau.cs.kieler.klighd.kgraph.KNode; +import de.cau.cs.kieler.klighd.krendering.KColor; import de.cau.cs.kieler.klighd.krendering.KText; import de.cau.cs.kieler.klighd.microlayout.Bounds; @@ -387,6 +388,12 @@ public static boolean isSelectable(final EObject viewElement) { */ public static final IProperty COLOR_PREFERENCES = new Property("klighd.colorPreferences", null); + /** + * Property to be set on the root node of the graph that defines a custom background color that the diagram + * canvas should use. Null means no custom background, a default white one will be used. + */ + public static final IProperty DIAGRAM_BACKGROUND = + new Property("klighd.diagramBackground", null); /** * Property determining whether this node should be rendered as a proxy.