import org.simantics.db.procedure.Listener;
import org.simantics.db.request.Read;
import org.simantics.diagram.stubs.DiagramResource;
+import org.simantics.g2d.canvas.Hints;
import org.simantics.g2d.canvas.ICanvasContext;
import org.simantics.g2d.diagram.IDiagram;
+import org.simantics.g2d.diagram.handler.DataElementMap;
import org.simantics.g2d.element.IElement;
import org.simantics.scenegraph.INode;
import org.simantics.scenegraph.profile.DataNodeMap;
public abstract class StyleBase<Result> implements Style {
private Object identity;
+ private double priority;
public StyleBase(Object identity) {
this.identity = identity;
return (T)identity;
}
+ public void setPriority(double priority) {
+ this.priority = priority;
+ }
+
+ public double getPriority() {
+ return priority;
+ }
+
@Override
public int hashCode() {
final int prime = 31;
StyleBaseData.getInstance().removeValue(new Tuple3(this, runtimeDiagram, object));
else
StyleBaseData.getInstance().putValue(new Tuple3(this, runtimeDiagram, object), result);
- observer.update();
+ observer.update(this, object);
}
/**
final INode node = map.getNode(item);
if (node == null) {
- evaluationContext.update();
+ evaluationContext.update(this, item);
// TODO: continue or return?
return;
}
System.out.println(StyleBase.this + ": applying style for item " + item + " and element " + node + " with result " + value);
applyStyleForNode(evaluationContext, node, value);
-
}
/**
cleanupItems(observer, diagram, listener.getItems().toArray());
diagram = null;
}
- observer.update();
+
+ //observer.update(); TODO: Check if this is required!
}
}
*/
@Override
public final void apply(Resource entry, Group group, final EvaluationContext evaluationContext) {
-
ICanvasContext context = evaluationContext.getConstant(ProfileKeys.CANVAS);
assert context.getThreadAccess().currentThreadAccess();
StyleBaseData data = StyleBaseData.getInstance();
data.applyRemovals(evaluationContext, this);
-
+
+ IDiagram diagram = evaluationContext.getConstant(ProfileKeys.DIAGRAM);
+ assert diagram != null;
+ DataElementMap emap = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
+
for (Object item : listener.getItems()) {
Result value = data.getValue(new Tuple3(this, evaluationContext.getResource(), item));
applyStyleForItem(evaluationContext, map, item, value);
+
+ IElement element = emap.getElement(diagram, item);
+ if (element != null)
+ element.setHint(Hints.KEY_DIRTY, Hints.VALUE_SG_DIRTY);
}
}
+ @Override
+ public final void apply2(Object item, final EvaluationContext evaluationContext) {
+ final DataNodeMap map = evaluationContext.getConstant(ProfileKeys.NODE_MAP);
+
+ StyleBaseData data = StyleBaseData.getInstance();
+
+ data.applyRemovals(evaluationContext, this);
+
+ Result value = data.getValue(new Tuple3(this, evaluationContext.getResource(), item));
+ applyStyleForItem(evaluationContext, map, item, value);
+
+ IDiagram diagram = evaluationContext.getConstant(ProfileKeys.DIAGRAM);
+ assert diagram != null;
+ DataElementMap emap = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
+ IElement element = emap.getElement(diagram, item);
+ if (element != null)
+ element.setHint(Hints.KEY_DIRTY, Hints.VALUE_SG_DIRTY);
+ }
+
/**
* This is ran when this profile entry gets deactivated after being first
* active. It allows cleaning up scene graph left-overs for the listened set
if (DebugPolicy.DEBUG_PROFILE_STYLE_ACTIVATION)
System.out.println(this + ".cleanupItems(" + evaluationContext + ", " + diagram + ", " + Arrays.toString(items));
+ IDiagram diagram = evaluationContext.getConstant(ProfileKeys.DIAGRAM);
+ assert diagram != null;
+ DataElementMap emap = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
+
for (Object item : items) {
cleanupStyleForItem(evaluationContext, map, item);
+
+ IElement element = emap.getElement(diagram, item);
+ if (element != null)
+ element.setHint(Hints.KEY_DIRTY, Hints.VALUE_SG_DIRTY);
}
}
});
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
import org.simantics.databoard.Bindings;
import org.simantics.datatypes.literal.Vec2d;
import org.simantics.scenegraph.utils.NodeUtil;
import org.simantics.scl.runtime.function.Function1;
import org.simantics.ui.colors.Colors;
+import org.simantics.utils.datastructures.Pair;
/**
* @author Antti Villberg
@Override
public void applyStyleForNode(EvaluationContext observer, INode _node, MonitorTextGridResult result) {
-
String value = result != null ? result.getText1() : null;
boolean enabled = result != null ? result.getEnabled() : false;
if (value != null && enabled) {
- // if (value != null && !value.isEmpty() && !value.trim().isEmpty()) {
-
- String value2 = result != null ? result.getText2() : null;
- String value3 = result != null ? result.getText3() : null;
-
- double spacing = result.getSpacing();
-
- final Function1<String, String> modifier = result != null ? result.getModifier() : null;
- final Function1<String, String> validator = result != null ? result.getValidator() : null;
- final Function1<Vec2d, Boolean> translator = result != null ? result.getTranslator() : null;
- final RVI rvi = result != null ? result.getRVI() : null;
-
- final TextGridNode node = ProfileVariables.claimChild(_node, "", "TextGridStyle", TextGridNode.class, observer);
- if (node == null)
+ Map<String, Pair<TextGridStyle, MonitorTextGridResult>> rows = observer.getProperty(_node, "rows");
+ if (rows == null) {
+ rows = new HashMap<String, Pair<TextGridStyle, MonitorTextGridResult>>();
+ observer.setProperty(_node, "rows", rows);
+ }
+ Pair<TextGridStyle, MonitorTextGridResult> oldResultPair = rows.get(result.getRowId());
+ if (oldResultPair != null && oldResultPair.first == this && oldResultPair.second.sameStructure(result)) {
return;
-
- // This assumes that this TextGridStyle instance will be devoted to
- // this row ID until the end of its life.
-// String id = result.getRowId();
-// System.out.println(this + " ID: " + id);
-// if (!id.equals(this.id)) {
-// System.out.println(this + " SET ID: " + this.id + " -> " + id);
-// this.id = id;
-// }
-
- Integer newRow = observer.getTemporaryProperty(_node, "location");
- if (newRow == null)
- newRow = 1;
-
- // Remove from existing row to add to another row if necessary.
- Integer row = getCurrentRowNumber(observer, _node);
- if (row != null && row != newRow) {
- String actualId = node.getRowId(row);
- String id = observer.getProperty(_node, rowIdKey());
- if (id.equals(actualId)) {
- node.removeRow(row);
- }
}
- row = newRow;
- node.setRowId(row, result.getRowId());
+ rows.put(rowIdKey(), new Pair<TextGridStyle, MonitorTextGridResult>(this, result));
- setCurrentRowNumber(observer, _node, result.getRowId(), row);
+ // FIXME: Improve performance by calling refreshAll only once after all text grid style changes have been applied
+ refreshAll(observer, _node);
+ } else {
+ cleanupStyleForNode(observer, _node);
+ }
+ }
- observer.setTemporaryProperty(_node, "location", row + 1);
+ private static final Comparator<Pair<TextGridStyle, MonitorTextGridResult>> ROW_PRIORITY_COMPARATOR =
+ (o1, o2) -> Double.compare(o1.first.getPriority(), o2.first.getPriority());
- node.setText(2, row, value2);
- node.setUp(result.getUp());
+ private static void refreshAll(EvaluationContext observer, INode _node) {
+ final TextGridNode node = ProfileVariables.claimChild(_node, "", "TextGridStyle", TextGridNode.class, observer);
+ if (node == null)
+ return;
- MonitorTextGridResult cache = node.getCache(1, row);
- if(cache != null && cache.sameStructure(result)) return;
+ int row = 0;
+ Map<String, Pair<TextGridStyle, MonitorTextGridResult>> rows = observer.getProperty(_node, "rows");
+ if (rows != null) {
+ List<Pair<TextGridStyle, MonitorTextGridResult>> sortedRows = rows.values().stream()
+ .sorted(ROW_PRIORITY_COMPARATOR)
+ .collect(Collectors.toList());
- node.setCache(1, row, result);
+ for (Pair<TextGridStyle, MonitorTextGridResult> resultPair : sortedRows) {
+ row++;
+ TextGridStyle style = resultPair.first;
+ MonitorTextGridResult result = resultPair.second;
- boolean isConnection = _node instanceof ConnectionNode;
+ String value = result != null ? result.getText1() : null;
+ String value2 = result != null ? result.getText2() : null;
+ String value3 = result != null ? result.getText3() : null;
- Rectangle2D elementBounds = isConnection ? EMPTY_BOUNDS : NodeUtil.getLocalElementBounds(_node);
- if(elementBounds == null) {
- new Exception("Cannot get local element bounds for node " + _node.toString()).printStackTrace();
- // This is here for checking why getLocalElementBounds failed in the debugger.
- NodeUtil.getLocalElementBounds(_node);
- return;
- }
+ double spacing = result.getSpacing();
- // System.err.println("elementBounds " + elementBounds);
- // System.err.println("parentTransform " + result.getParentTransform());
-
- AffineTransform at = getTransform(_node,result.getParentTransform(), elementBounds, row, result.getUp());
- Vec2d offset = result.getOffset();
-
- Point2D[] cellOffsets = getCellOffsets();
-
- AffineTransform at1 = new AffineTransform(at);
- at1.translate(cellOffsets[0].getX(),cellOffsets[0].getY());
- AffineTransform at2 = new AffineTransform(at);
- at2.translate(cellOffsets[1].getX()+spacing,cellOffsets[1].getY());
- AffineTransform at3 = new AffineTransform(at);
- at3.translate(cellOffsets[2].getX()+spacing,cellOffsets[2].getY());
-
- at1.translate(offset.x, offset.y);
- at2.translate(offset.x, offset.y);
- at3.translate(offset.x, offset.y);
-
- node.setTransform(1, row, at1);
- node.setTransform(2, row, at2);
- node.setTransform(3, row, at3);
-
- Alignment[] alignments = result.getAlignments();
- if(alignments != null) {
- node.setHorizontalAlignment(1, row, (byte) alignments[0].ordinal());
- node.setHorizontalAlignment(2, row, (byte) alignments[1].ordinal());
- node.setHorizontalAlignment(3, row, (byte) alignments[2].ordinal());
- } else {
- node.setHorizontalAlignment(1, row, (byte) getAlignment(1).ordinal());
- node.setHorizontalAlignment(2, row, (byte) getAlignment(2).ordinal());
- node.setHorizontalAlignment(3, row, (byte) getAlignment(3).ordinal());
- }
+ final Function1<String, String> modifier = result != null ? result.getModifier() : null;
+ final Function1<String, String> validator = result != null ? result.getValidator() : null;
+ final Function1<Vec2d, Boolean> translator = result != null ? result.getTranslator() : null;
+ final RVI rvi = result != null ? result.getRVI() : null;
- Alignment[] verticalAlignments = result.getVerticalAlignments();
- if(verticalAlignments != null) {
- node.setVerticalAlignment(1, row, (byte) verticalAlignments[0].ordinal());
- node.setVerticalAlignment(2, row, (byte) verticalAlignments[1].ordinal());
- node.setVerticalAlignment(3, row, (byte) verticalAlignments[2].ordinal());
- } else {
- node.setVerticalAlignment(1, row, (byte) getVerticalAlignment(1).ordinal());
- node.setVerticalAlignment(2, row, (byte) getVerticalAlignment(2).ordinal());
- node.setVerticalAlignment(3, row, (byte) getVerticalAlignment(3).ordinal());
- }
+ node.setRowId(row, result.getRowId());
- node.setZIndex(3000);
+ //setCurrentRowNumber(observer, _node, result.getRowId(), row);
- org.simantics.common.color.Color color = result.getColor();
- Color awtColor = color != null ? Colors.awt(color) : Color.DARK_GRAY;
- Color bgColor = getBackgroundColor();
- Font font = getFont();
+ //observer.setTemporaryProperty(_node, "location", row + 1);
- setTextNodeData(node, 1, row, value, font, awtColor, bgColor);
- setTextNodeData(node, 2, row, value2, result.getPending(), font, awtColor, bgColor);
- setTextNodeData(node, 3, row, value3, font, awtColor, bgColor);
+ node.setText(2, row, value2);
+ node.setUp(result.getUp());
- node.setEditable(1, row, false);
- node.setForceEventListening(2, row, true);
- node.setEditable(2, row, modifier != null);
- node.setEditable(3, row, false);
+ //MonitorTextGridResult cache = node.getCache(1, row);
+ //if(cache != null && cache.sameStructure(result)) return;
- final int finalRow = row;
+ node.setCache(1, row, result);
- if (modifier != null) {
- node.setTextListener(2, row, new ITextListener() {
- @Override
- public void textChanged() {}
+ boolean isConnection = _node instanceof ConnectionNode;
- @Override
- public void textEditingStarted() {}
+ Rectangle2D elementBounds = isConnection ? EMPTY_BOUNDS : NodeUtil.getLocalElementBounds(_node);
+ if (elementBounds == null) {
+ new Exception("Cannot get local element bounds for node " + _node.toString()).printStackTrace();
+ // This is here for checking why getLocalElementBounds failed in the debugger.
+ NodeUtil.getLocalElementBounds(_node);
+ return;
+ }
+
+ //System.err.println("elementBounds " + elementBounds);
+ //System.err.println("parentTransform " + result.getParentTransform());
+
+ AffineTransform at = style.getTransform(_node,result.getParentTransform(), elementBounds, row, result.getUp());
+ Vec2d offset = result.getOffset();
+
+ Point2D[] cellOffsets = style.getCellOffsets();
+
+ AffineTransform at1 = new AffineTransform(at);
+ at1.translate(cellOffsets[0].getX(),cellOffsets[0].getY());
+ AffineTransform at2 = new AffineTransform(at);
+ at2.translate(cellOffsets[1].getX()+spacing,cellOffsets[1].getY());
+ AffineTransform at3 = new AffineTransform(at);
+ at3.translate(cellOffsets[2].getX()+spacing,cellOffsets[2].getY());
+
+ at1.translate(offset.x, offset.y);
+ at2.translate(offset.x, offset.y);
+ at3.translate(offset.x, offset.y);
+
+ node.setTransform(1, row, at1);
+ node.setTransform(2, row, at2);
+ node.setTransform(3, row, at3);
+
+ Alignment[] alignments = result.getAlignments();
+ if(alignments != null) {
+ node.setHorizontalAlignment(1, row, (byte) alignments[0].ordinal());
+ node.setHorizontalAlignment(2, row, (byte) alignments[1].ordinal());
+ node.setHorizontalAlignment(3, row, (byte) alignments[2].ordinal());
+ } else {
+ node.setHorizontalAlignment(1, row, (byte) style.getAlignment(1).ordinal());
+ node.setHorizontalAlignment(2, row, (byte) style.getAlignment(2).ordinal());
+ node.setHorizontalAlignment(3, row, (byte) style.getAlignment(3).ordinal());
+ }
- @Override
- public void textEditingCancelled() {
- }
+ Alignment[] verticalAlignments = result.getVerticalAlignments();
+ if(verticalAlignments != null) {
+ node.setVerticalAlignment(1, row, (byte) verticalAlignments[0].ordinal());
+ node.setVerticalAlignment(2, row, (byte) verticalAlignments[1].ordinal());
+ node.setVerticalAlignment(3, row, (byte) verticalAlignments[2].ordinal());
+ } else {
+ node.setVerticalAlignment(1, row, (byte) style.getVerticalAlignment(1).ordinal());
+ node.setVerticalAlignment(2, row, (byte) style.getVerticalAlignment(2).ordinal());
+ node.setVerticalAlignment(3, row, (byte) style.getVerticalAlignment(3).ordinal());
+ }
- @Override
- public void textEditingEnded() {
+ node.setZIndex(3000);
+
+ org.simantics.common.color.Color color = result.getColor();
+ Color awtColor = color != null ? Colors.awt(color) : Color.DARK_GRAY;
+ Color bgColor = style.getBackgroundColor();
+ Font font = style.getFont();
+ style.setTextNodeData(node, 1, row, value, font, awtColor, bgColor);
+ style.setTextNodeData(node, 2, row, value2, result.getPending(), font, awtColor, bgColor);
+ style.setTextNodeData(node, 3, row, value3, font, awtColor, bgColor);
+
+ node.setEditable(1, row, false);
+ node.setForceEventListening(2, row, true);
+ node.setEditable(2, row, modifier != null);
+ node.setEditable(3, row, false);
+
+ final int finalRow = row;
+
+ if (modifier != null) {
+ node.setTextListener(2, row, new ITextListener() {
+ @Override
+ public void textChanged() {}
+
+ @Override
+ public void textEditingStarted() {}
+
+ @Override
+ public void textEditingCancelled() {
+ }
+
+ @Override
+ public void textEditingEnded() {
+
+ TextNode t = node.get(2, finalRow);
+ if (t == null)
+ return;
+
+ if(!t.getText().equals(t.getTextBeforeEdit()))
+ modifier.apply(t.getText());
+
+ }
+ });
+ } else {
+ node.setTextListener(2, row, null);
+ }
- TextNode t = node.get(2, finalRow);
- if (t == null)
- return;
+ node.setInputValidator(2, row, validator);
+ node.setTranslator(translator);
- if(!t.getText().equals(t.getTextBeforeEdit()))
- modifier.apply(t.getText());
+ node.setRVI(2, row, rvi);
- }
- });
- } else {
- node.setTextListener(2, row, null);
+ style.postProcessNode(node, row);
}
-
- node.setInputValidator(2, row, validator);
- node.setTranslator(translator);
-
- node.setRVI(2, row, rvi);
-
- postProcessNode(node, row);
-
- } else {
- cleanupStyleForNode(observer, _node);
+ }
+ // remove excess rows
+ int rowCount = node.computeRows();
+ while (row < rowCount) {
+ row++;
+ node.removeRow(row);
}
}
@Override
protected void cleanupStyleForNode(EvaluationContext observer, INode _node) {
- Integer row = getCurrentRowNumber(observer, _node);
- //System.out.println(this + " cleanup(" + id + ", " + row + ")");
- //System.out.println(element);
- if (row == null)
- return;
- clearCurrentRowNumber(observer, _node);
- TextGridNode node = ProfileVariables.browseChild(_node, "TextGridStyle");
- if (node != null)
- node.removeRow(row);
- }
-
- private Integer getCurrentRowNumber(EvaluationContext observer, INode _node) {
- String rowId = observer.getProperty(_node, rowIdKey());
- return observer.getProperty(_node, rowId);
- }
-
- private void setCurrentRowNumber(EvaluationContext observer, INode _node, String rowId, int row) {
- // Mapping style identity -> rowId (resourceId)
- observer.setProperty(_node, rowIdKey(), rowId);
- // Mapping rowId (resourceId) -> row number
- observer.setProperty(_node, rowId, row);
- }
-
- private void clearCurrentRowNumber(EvaluationContext observer, INode _node) {
- String rowId = observer.getProperty(_node, rowIdKey());
- if(rowId != null) {
- observer.setProperty(_node, rowIdKey(), null);
- Integer row = observer.getProperty(_node, rowId);
- if(row != null) {
- observer.setProperty(_node, rowId, null);
+ Map<String, Pair<Double, MonitorTextGridResult>> rows = observer.getProperty(_node, "rows");
+ if (rows != null) {
+ rows.remove(rowIdKey());
+ if (rows.isEmpty()) {
+ observer.setProperty(_node, "rows", null);
}
}
+ refreshAll(observer, _node);
}
protected void postProcessNode(TextGridNode node, int row) {
*******************************************************************************/
package org.simantics.scenegraph.profile.common;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.simantics.scenegraph.g2d.G2DSceneGraph;
import org.simantics.scenegraph.profile.EvaluationContext;
import org.simantics.scenegraph.profile.ProfileEntry;
+import org.simantics.scenegraph.profile.Style;
import org.simantics.scenegraph.profile.impl.DebugPolicy;
import org.simantics.scenegraph.profile.impl.ProfileActivationListener;
import org.simantics.scenegraph.profile.request.RuntimeProfileActiveEntries;
+import org.simantics.utils.datastructures.Pair;
import org.simantics.utils.datastructures.disposable.IDisposable;
import org.simantics.utils.threads.IThreadWorkQueue;
import org.simantics.utils.threads.ThreadUtils;
private final Runnable notification;
private final G2DSceneGraph sceneGraph;
- private boolean dirty = true;
- private boolean disposed = false;
+ private volatile boolean dirty = true;
+ private volatile boolean disposed = false;
+ private List<Pair<Style, Object>> updates = new ArrayList<>();
+ private boolean updateAll;
+
private ProfileActivationListener activationListener;
private Map<String, Object> constants = new HashMap<String, Object>();
}
@Override
- public void update() {
+ public void update(Style style, Object item) {
if (DebugPolicy.DEBUG_PROFILE_OBSERVER_UPDATE)
System.out.println("Profile observer marked dirty.");
+
+ updates.add(Pair.make(style, item));
+ //updateAll = true;
dirty = true;
}
+ public void update() {
+ updateAll = true;
+ dirty = true;
+ }
+
private void perform() {
dirty = false;
if (DebugPolicy.DEBUG_PROFILE_OBSERVER_UPDATE)
return;
ThreadUtils.asyncExec(thread, new Runnable() {
-
-// private void init(INode node) {
-// //ProfileVariables.init(node, ProfileObserver.this);
-//// NodeUtil.forChildren(node, new NodeProcedure<Object>() {
-//// @Override
-//// public Object execute(INode node, String id) {
-//// init(node);
-//// return null;
-//// }
-//// }, null);
-// }
-
@Override
public void run() {
-
if (isDisposed())
return;
temporaryProperties.clear();
-// init(sceneGraph);
-
-// for(IElement e : diagram.getElements()) {
-// Node node = NodeUtils.
-// Variables.init(e, ProfileObserver.this);
-// }
-
- for(ProfileEntry e : entries) {
- if (DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM)
- System.out.println("Apply profile entry: " + e);
- e.apply(ProfileObserver.this);
+ long t0 = DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM ? System.nanoTime() : 0L;
+
+ if (updateAll) {
+ for(ProfileEntry e : entries) {
+ if (DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM)
+ System.out.println("Apply profile entry: " + e);
+ e.apply(ProfileObserver.this);
+ }
+ updateAll = false;
+ updates.clear();
+ } else {
+ List<Pair<Style, Object>> updatesCopy = new ArrayList<>(updates);
+ updates.clear();
+ for (Pair<Style, Object> update : updatesCopy) {
+ Style style = update.first;
+ Object item = update.second;
+
+ style.apply2(item, ProfileObserver.this);
+ }
}
+ if (DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM) {
+ long t1 = System.nanoTime();
+ System.out.println((t1-t0) / 1e6);
+ }
+
if(dirty) {
sceneGraph.setPending(ProfileObserver.this);
// System.err.println("setPending, dirty=true");
// canvas.getContentContext().setDirty();
// Something is underway, schedule update
- if(dirty) {
- Simantics.async(new Runnable() {
-
- @Override
- public void run() {
-
- if (isDisposed()) return;
-
- if(dirty) perform();
-
- }
-
- }, 100, TimeUnit.MILLISECONDS);
+ if (dirty) {
+ Simantics.async(() -> {
+ if (isDisposed()) return;
+ if (dirty) perform();
+ }, 100, TimeUnit.MILLISECONDS);
}
-
}
});
}