From 5277008b6f06e11483b4029784b8096eeb73aa05 Mon Sep 17 00:00:00 2001 From: Niklas Rentz Date: Sat, 28 Sep 2024 21:42:32 +0200 Subject: [PATCH 1/3] copy properties to the SModel already during initial SModel generation for client-only layout. --- .../klighd/lsp/KGraphDiagramGenerator.xtend | 5 ++++ .../kieler/klighd/lsp/model/KGraphModel.xtend | 3 ++- .../klighd/lsp/utils/KGraphMappingUtil.xtend | 24 +++++++++---------- 3 files changed, 19 insertions(+), 13 deletions(-) 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 fad21258f..a92072d29 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 @@ -60,6 +60,7 @@ import org.eclipse.sprotty.SPort import org.eclipse.sprotty.xtext.IDiagramGenerator import org.eclipse.xtend.lib.annotations.Accessors import org.eclipse.xtext.util.CancelIndicator +import de.cau.cs.kieler.klighd.lsp.utils.KGraphMappingUtil /** * A diagram generator that can create Sprotty {@link SGraph} from any {@link Object} that has a registered view @@ -259,6 +260,7 @@ class KGraphDiagramGenerator implements IDiagramGenerator { nodeElement.data = node.data.filter[KRenderingLibrary.isAssignableFrom(it.class)].toList setProperties(nodeElement, node) + KGraphMappingUtil.mapProperties(node, nodeElement) findSpecialRenderings(filteredData) val renderingContextData = RenderingContextData.get(node) @@ -311,6 +313,7 @@ class KGraphDiagramGenerator implements IDiagramGenerator { val renderings = edge.data.filter[KRendering.isAssignableFrom(it.class)].toList + KGraphMappingUtil.mapProperties(edge, edgeElement) findSpecialRenderings(renderings) edgeElement.children.addAll(createLabels(edge.labels)) edgeElement.junctionPoints = edge.getProperty(CoreOptions.JUNCTION_POINTS) @@ -332,6 +335,7 @@ class KGraphDiagramGenerator implements IDiagramGenerator { val renderings = port.data.filter [ KRendering.isAssignableFrom(it.class)].toList + KGraphMappingUtil.mapProperties(port, portElement) findSpecialRenderings(renderings) portElement.children.addAll(createLabels(port.labels)) @@ -353,6 +357,7 @@ class KGraphDiagramGenerator implements IDiagramGenerator { val renderings = label.data.filter[KRendering.isAssignableFrom(it.class)].toList + KGraphMappingUtil.mapProperties(label, labelElement) findSpecialRenderings(renderings) // activate the element by default if it does not have an active/inactive status yet. val renderingContextData = RenderingContextData.get(label) diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/model/KGraphModel.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/model/KGraphModel.xtend index fa4a601b7..d8f628809 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/model/KGraphModel.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/model/KGraphModel.xtend @@ -3,7 +3,7 @@ * * http://rtsys.informatik.uni-kiel.de/kieler * - * Copyright 2018-2022 by + * Copyright 2018-2024 by * + Kiel University * + Department of Computer Science * + Real-Time and Embedded Systems Group @@ -39,6 +39,7 @@ abstract interface SKElement { def List getData() def void setData(List data) def HashMap getProperties() + def void setProperties(HashMap theProperties) } /** diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/utils/KGraphMappingUtil.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/utils/KGraphMappingUtil.xtend index bf5a569e6..388cfe822 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/utils/KGraphMappingUtil.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/utils/KGraphMappingUtil.xtend @@ -23,6 +23,7 @@ import de.cau.cs.kieler.klighd.kgraph.KLabel import de.cau.cs.kieler.klighd.kgraph.KNode import de.cau.cs.kieler.klighd.kgraph.KPort import de.cau.cs.kieler.klighd.kgraph.KShapeLayout +import de.cau.cs.kieler.klighd.kgraph.util.KGraphUtil import de.cau.cs.kieler.klighd.lsp.model.SKEdge import de.cau.cs.kieler.klighd.lsp.model.SKElement import de.cau.cs.kieler.klighd.lsp.model.SKLabel @@ -39,7 +40,6 @@ import org.eclipse.sprotty.Dimension import org.eclipse.sprotty.Point import org.eclipse.sprotty.SModelElement import org.eclipse.sprotty.SShapeElement -import de.cau.cs.kieler.klighd.kgraph.util.KGraphUtil /** * A helper class containing static methods for mapping of KGraph and SGraph bounds. @@ -108,15 +108,7 @@ class KGraphMappingUtil { skEdge.junctionPoints.addAllAsCopies(0, kEdge.getProperty(CoreOptions.JUNCTION_POINTS)) skEdge.junctionPoints.offset(new KVector(leftInset, topInset)) - // map all properties excepts those that are blacklisted - // also include external whitelisted properties - var properties = kEdge.allProperties; - - for (propertyKVPair : properties.entrySet()) { - if (keepProperty(propertyKVPair.key)) { - skEdge.properties.put(propertyKVPair.key.id, propertyKVPair.value) - } - } + mapProperties(kEdge, skEdge) } /** @@ -138,11 +130,19 @@ class KGraphMappingUtil { skNode.position = new Point(kNode.xpos + leftInset, kNode.ypos + topInset) skNode.size = new Dimension(kNode.width, kNode.height) - var properties = kNode.allProperties; + mapProperties(kNode, skNode) + } + + /** + * Maps the properties of the KGraphElement to the corresponding SModelElement. + * Excepts those that are blacklisted, but also include whitelisted external properties. + */ + static def mapProperties(KGraphElement kElement, SKElement sElement) { + val properties = kElement.allProperties for (propertyKVPair : properties.entrySet()) { if (keepProperty(propertyKVPair.key)) { - skNode.properties.put(propertyKVPair.key.id, propertyKVPair.value) + sElement.properties.put(propertyKVPair.key.id, propertyKVPair.value) } } } From 1dc8b7dc7e88345f2c0f0e2be5d107240f85c4cb Mon Sep 17 00:00:00 2001 From: Niklas Rentz Date: Mon, 30 Sep 2024 20:15:20 +0200 Subject: [PATCH 2/3] Split preparing rendering IDs to happen right after model generation (specifically, before any layout stuff) --- .../klighd/lsp/KGraphDiagramUpdater.xtend | 2 + .../klighd/lsp/KGraphLayoutEngine.xtend | 2 +- .../klighd/lsp/utils/RenderingPreparer.xtend | 118 ++++++++++++++---- 3 files changed, 96 insertions(+), 26 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 7c6905338..cf9501e9c 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.lsp.utils.RenderingPreparer import de.cau.cs.kieler.klighd.util.KlighdSynthesisProperties import java.util.HashSet import java.util.List @@ -316,6 +317,7 @@ class KGraphDiagramUpdater extends DiagramUpdater { var SGraph sGraph = null; synchronized (diagramState) { sGraph = diagramGenerator.toSGraph(viewContext.viewModel, uri, cancelIndicator) + RenderingPreparer.prepareRenderingIDs(viewContext.viewModel, diagramGenerator.getKGraphToSModelElementMap) } if (incrementalDiagramGenerator) { val requestManager = new KGraphDiagramPieceRequestManager(diagramGenerator as KGraphIncrementalDiagramGenerator) diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphLayoutEngine.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphLayoutEngine.xtend index c600be067..23f119011 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphLayoutEngine.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphLayoutEngine.xtend @@ -87,7 +87,7 @@ class KGraphLayoutEngine extends ElkLayoutEngine { synchronized (kGraphContext.viewModel) { lightDiagramLayoutConfig.performLayout - RenderingPreparer.prepareRendering(kGraphContext.viewModel, diagramState.getKGraphToSModelElementMap(uri)) + RenderingPreparer.prepareRenderingLayout(kGraphContext.viewModel, diagramState.getKGraphToSModelElementMap(uri)) } } diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/utils/RenderingPreparer.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/utils/RenderingPreparer.xtend index 6a3f8c20f..dc45e9e14 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/utils/RenderingPreparer.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/utils/RenderingPreparer.xtend @@ -64,6 +64,93 @@ import static extension de.cau.cs.kieler.klighd.lsp.utils.SprottyProperties.* * @author nre */ final class RenderingPreparer { + + /** + * Prepares the rendering IDs of a KGraph to be generated for an external viewer. + * + * + * @param element The parent element containing the graph to calculate all rendering IDs for. + * @param kGraphToSGraph A map for identifying the SGraph element for each KGraph element in this graph. + */ + static def void prepareRenderingIDs(KGraphElement element, Map kGraphToSGraph) { + // calculate the IDs of all renderings: + for (var int i = 0; i < element.data.size; i++) { + val data = element.data.get(i) + switch(data) { + KRenderingLibrary: { + // The library needs to generate ids for all later KRenderingRefs to refer to. + for (var int j = 0; j < data.renderings.size; j++) { + val rendering = data.renderings.get(j) + if (rendering instanceof KRendering) { + KRenderingIdGenerator.generateIdsRecursive(rendering, "$$lib$$", j) + } + } + } + KRenderingRef: { + // rendering refs refer to the referred ID + data.renderingId = kGraphToSGraph.get(element)?.id + data.rendering.renderingId + } + KRendering: { + // every rendering needs an ID, generate it here + KRenderingIdGenerator.generateIdsRecursive(data, kGraphToSGraph.get(element)?.id + "$$", i) + } + } + } + + // Recursively call this method for every child KGraphElement of this. + // (all labels, child nodes, outgoing edges and ports) + + if (element instanceof KLabeledGraphElement) { + for (label : element.labels) { + prepareRenderingIDs(label, kGraphToSGraph) + } + } + if (element instanceof KNode) { + // Do not recurse generating IDs if the element is not expanded, as there won't be any SGraph generated for + // it. + var boolean isExpanded + val renderingContextData = RenderingContextData.get(element) + if (renderingContextData.hasProperty(SprottyProperties.EXPANDED)) { + isExpanded = renderingContextData.getProperty(SprottyProperties.EXPANDED) + } else { + // If the expanded property does not exist yet, use the initial expansion. + isExpanded = element.getProperty(KlighdProperties.EXPAND) + } + + if (isExpanded) { + for (node : element.children) { + prepareRenderingIDs(node, kGraphToSGraph) + } + } + for (edge : element.outgoingEdges) { + // not expanded => edge must not have the target node inside the non-expanded + if (isExpanded || !KGraphUtil.isDescendant(edge.target, element)) { + prepareRenderingIDs(edge, kGraphToSGraph) + } + } + for (port : element.ports) { + prepareRenderingIDs(port, kGraphToSGraph) + } + } + + // Also prepare the IDs of all proxy-renderings + val proxyRendering = element.getProperty(KlighdProperties.PROXY_VIEW_PROXY_RENDERING) + if (element.getProperty(KlighdProperties.PROXY_VIEW_RENDER_NODE_AS_PROXY) && proxyRendering !== null) { + for (var int i = 0; i < proxyRendering.size; i++) { + val data = proxyRendering.get(i) + switch(data) { + KRenderingRef: { + // rendering refs refer to the referred ID + data.renderingId = kGraphToSGraph.get(element)?.id + data.rendering.renderingId + } + KRendering: { + // every rendering needs an ID, generate it here + KRenderingIdGenerator.generateIdsRecursive(data, kGraphToSGraph.get(element)?.id + "$$", i) + } + } + } + } + } /** * Prepares a KGraphElement to be rendered in an external viewer. @@ -74,29 +161,18 @@ final class RenderingPreparer { * In case of a {@link KRenderingRef} the bounds and decoration are persisted for every referenced rendering as a map * inside the properties of the reference. * For example: <id of the rendering in the library: bounds in this instance> - * Furthermore, for every rendering a unique ID is generated. * Finally, modifiable styles defined by the synthesis are processed for the rendering. * * @param element The parent element containing the graph to calculate all rendering bounds for. * @param kGraphToSGraph A map for identifying the SGraph element for each KGraph element in this graph. */ - static def void prepareRendering(KGraphElement element, Map kGraphToSGraph) { + static def void prepareRenderingLayout(KGraphElement element, Map kGraphToSGraph) { // calculate the sizes of all renderings: for (var int i = 0; i < element.data.size; i++) { val data = element.data.get(i) switch(data) { - KRenderingLibrary: { - // The library needs to generate ids for all later KRenderingRefs to refer to, but no own bounds, - // since these are generic renderings. - for (var int j = 0; j < data.renderings.size; j++) { - val rendering = data.renderings.get(j) - if (rendering instanceof KRendering) { - KRenderingIdGenerator.generateIdsRecursive(rendering, "$$lib$$", j) - } - } - } KRenderingRef: { - // all references to KRenderings need to place a map with the ids of the renderings and their + // all references to KRenderings need to place a map with their // sizes and their decoration in this case in the properties of the reference. var boundsMap = new HashMap var decorationMap = new HashMap @@ -105,12 +181,8 @@ final class RenderingPreparer { data.properties.put(CALCULATED_BOUNDS_MAP, boundsMap) // and the decorationMap data.properties.put(CALCULATED_DECORATION_MAP, decorationMap) - // remember the id of the rendering in the reference - data.renderingId = kGraphToSGraph.get(element)?.id + data.rendering.renderingId } KRendering: { - // every rendering needs an ID, generate it here - KRenderingIdGenerator.generateIdsRecursive(data, kGraphToSGraph.get(element)?.id + "$$", i) handleKRendering(element, data, null, null) } } @@ -121,7 +193,7 @@ final class RenderingPreparer { if (element instanceof KLabeledGraphElement) { for (label : element.labels) { - prepareRendering(label, kGraphToSGraph) + prepareRenderingLayout(label, kGraphToSGraph) } } if (element instanceof KNode) { @@ -138,17 +210,17 @@ final class RenderingPreparer { if (isExpanded) { for (node : element.children) { - prepareRendering(node, kGraphToSGraph) + prepareRenderingLayout(node, kGraphToSGraph) } } for (edge : element.outgoingEdges) { // not expanded => edge must not have the target node inside the non-expanded if (isExpanded || !KGraphUtil.isDescendant(edge.target, element)) { - prepareRendering(edge, kGraphToSGraph) + prepareRenderingLayout(edge, kGraphToSGraph) } } for (port : element.ports) { - prepareRendering(port, kGraphToSGraph) + prepareRenderingLayout(port, kGraphToSGraph) } } @@ -168,13 +240,9 @@ final class RenderingPreparer { data.properties.put(CALCULATED_BOUNDS_MAP, boundsMap) // and the decorationMap data.properties.put(CALCULATED_DECORATION_MAP, decorationMap) - // remember the id of the rendering in the reference - data.renderingId = kGraphToSGraph.get(element)?.id + data.rendering.renderingId } KRendering: { - // every rendering needs an ID, generate it here - KRenderingIdGenerator.generateIdsRecursive(data, kGraphToSGraph.get(element)?.id + "$$", i) if (data.eContainer instanceof KNode) { // Calculate the size and layout of the proxy first. val parent = data.eContainer as KNode From fe13113548a6fdbfe9cfbc1ad06f5ba29ebbcb8a Mon Sep 17 00:00:00 2001 From: Niklas Rentz Date: Tue, 5 Nov 2024 14:56:47 +0100 Subject: [PATCH 3/3] refreshDiagramAction may now change viewer options. --- .../de/cau/cs/kieler/klighd/lsp/KGraphDiagramGenerator.xtend | 4 ++-- .../de/cau/cs/kieler/klighd/lsp/KGraphDiagramServer.xtend | 1 + .../src/de/cau/cs/kieler/klighd/lsp/model/Actions.xtend | 5 ++++- 3 files changed, 7 insertions(+), 3 deletions(-) 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 a92072d29..aa2ecb3c6 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 @@ -3,7 +3,7 @@ * * http://rtsys.informatik.uni-kiel.de/kieler * - * Copyright 2018-2021 by + * Copyright 2018-2024 by * + Kiel University * + Department of Computer Science * + Real-Time and Embedded Systems Group @@ -40,6 +40,7 @@ import de.cau.cs.kieler.klighd.lsp.model.SKLabel import de.cau.cs.kieler.klighd.lsp.model.SKNode import de.cau.cs.kieler.klighd.lsp.model.SKPort import de.cau.cs.kieler.klighd.lsp.utils.KGraphElementIdGenerator +import de.cau.cs.kieler.klighd.lsp.utils.KGraphMappingUtil import de.cau.cs.kieler.klighd.lsp.utils.SprottyProperties import de.cau.cs.kieler.klighd.util.KlighdPredicates import de.cau.cs.kieler.klighd.util.KlighdProperties @@ -60,7 +61,6 @@ import org.eclipse.sprotty.SPort import org.eclipse.sprotty.xtext.IDiagramGenerator import org.eclipse.xtend.lib.annotations.Accessors import org.eclipse.xtext.util.CancelIndicator -import de.cau.cs.kieler.klighd.lsp.utils.KGraphMappingUtil /** * A diagram generator that can create Sprotty {@link SGraph} from any {@link Object} that has a registered view 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 b6623fdfa..b44aa6136 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 @@ -353,6 +353,7 @@ class KGraphDiagramServer extends LanguageAwareDiagramServer { * Tells the server that the diagram should be refreshed. */ protected def handle(RefreshDiagramAction action) { + getOptions().putAll(action.options) updateDiagram() return } 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 407e853a3..14e874f37 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 @@ -3,7 +3,7 @@ * * http://rtsys.informatik.uni-kiel.de/kieler * - * Copyright 2018-2022 by + * Copyright 2018-2024 by * + Kiel University * + Department of Computer Science * + Real-Time and Embedded Systems Group @@ -18,6 +18,7 @@ package de.cau.cs.kieler.klighd.lsp.model import de.cau.cs.kieler.klighd.krendering.KImage import java.util.List +import java.util.Map import java.util.Set import java.util.function.Consumer import org.eclipse.sprotty.Action @@ -213,6 +214,8 @@ class RefreshDiagramAction implements Action { public static val KIND = 'refreshDiagram' String kind = KIND + Map options + new() {} new(Consumer initializer) { initializer.accept(this)