package org.simantics.district.network.ui.nodes; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; import org.simantics.district.network.ui.styles.DistrictNetworkHoverInfoStyle; import org.simantics.maps.MapScalingTransform; import org.simantics.scenegraph.INode; import org.simantics.scenegraph.NodeException; import org.simantics.scenegraph.ParentNode; import org.simantics.scenegraph.g2d.G2DNode; import org.simantics.scenegraph.g2d.G2DParentNode; import org.simantics.scenegraph.g2d.nodes.spatial.RTreeNode; import org.simantics.scenegraph.profile.common.ProfileVariables; import org.simantics.scenegraph.utils.DPIUtil; import org.simantics.scenegraph.utils.NodeUtil; import org.simantics.scl.runtime.Lists; import org.simantics.scl.runtime.tuple.Tuple3; public class DistrictNetworkHoverInfoNode extends G2DNode implements HoverSensitiveNode, DeferredNode { private static final Font FONT = new Font("Tahoma", Font.PLAIN, (int)(DPIUtil.upscale(9) * MapScalingTransform.getScaleY() + 0.5)); private static final long serialVersionUID = 1L; public static final String NODE_KEY = "DISTRICT_NETWORK_HOVER_INFO"; private static final int W1 = 50; private static final int W2 = 30; private static final int PAD = 5; private List labels; @SuppressWarnings("unused") private Point2D origin; private boolean hover = false; private Point2D mousePosition; private static AtomicReference activeNode = new AtomicReference<>(); @Override public void render(Graphics2D g) { ParentNode root = (ParentNode) NodeUtil.getNearestParentOfType(this, RTreeNode.class); DeferredRenderingNode deferred = root != null ? (DeferredRenderingNode) root.getNode(DistrictNetworkHoverInfoStyle.HOVER_INFO_DEFERRED) : null; if (deferred != null) deferred.deferNode(g.getTransform(), this); else renderDeferred(g); } @Override public void renderDeferred(Graphics2D g) { if (!hover || activeNode.get() == null) return; AffineTransform ot = g.getTransform(); Font of = g.getFont(); doRender(g); g.setFont(of); g.setTransform(ot); } private void doRender(Graphics2D g) { AffineTransform tt = getTransform(); g.transform(tt); if (mousePosition == null) return; g.translate(mousePosition.getX(), mousePosition.getY()); //g.translate(origin.getX(), origin.getY()); double scale = 1.5 / g.getTransform().getScaleX(); g.scale(scale, scale); g.setFont(FONT); double rowHeight = g.getFontMetrics().getHeight() * 1.1; // let's calculate the max width Optional max = labels.stream().map(t -> g.getFontMetrics().stringWidth((String) t.c2)).max(Comparator.naturalOrder()); int width = max.orElse(10); g.setColor(Color.WHITE); int totalHeight = (int)Math.round(rowHeight * labels.size()); g.fillRect(-(W1 + PAD + W2 + 5), -(totalHeight + (int)Math.round(rowHeight)), (W1 + PAD + W2 + width + 10), totalHeight + 5); g.setColor(Color.BLACK); for (Tuple3 t : labels) { g.translate(0.f, -rowHeight); if (t.c0 != null) { g.drawString((String) t.c0, -(W1 + PAD + W2), 0.f); } if (t.c1 != null) { int width1 = g.getFontMetrics().stringWidth((String) t.c1); g.drawString((String) t.c1, - width1, 0.f); } if (t.c2 != null) { g.drawString((String) t.c2, PAD, 0.f); } } } @Override public Rectangle2D getBoundsInLocal() { return null; } @SuppressWarnings("unchecked") public void setLabels(List list) { this.labels = Lists.reverse(list); } public void setOrigin(Point2D origin) { this.origin = origin; } @Override public boolean hover(boolean hover, boolean isConnectionTool) { // hover = hover && activeNode.updateAndGet(current -> current == null ? this : current) == this; boolean changed = hover != this.hover; this.hover = hover; // // if (changed) { // if (!hover) activeNode.updateAndGet(current -> current == this ? null : current); // repaint(); // } // return changed; } @Override public void setMousePosition(Point2D p) { mousePosition = p; } @Override public void delete() { super.delete(); activeNode.set(null); } public void hover2(G2DParentNode hoveredNode) { ParentNode root = (ParentNode) NodeUtil.getNearestParentOfType(parent, RTreeNode.class); if (root != null) { INode child = ProfileVariables.browseChild(root, ""); if(child == null) { throw new NullPointerException("Scenegraph child node was not found: " + ""); } INode existing = NodeUtil.getChildById(child, DistrictNetworkHoverInfoStyle.HOVER_INFO_DEFERRED); if (existing == null) { if (child instanceof ParentNode) { existing = ((ParentNode) child).addNode(DistrictNetworkHoverInfoStyle.HOVER_INFO_DEFERRED, DeferredRenderingNode.class); ((DeferredRenderingNode)existing).setZIndex(Integer.MAX_VALUE); } else { throw new NodeException("Cannot claim child node for non-parent-node " + child); } } } activeNode.set(hoveredNode); repaint(); } }