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.maps.MapScalingTransform; import org.simantics.scenegraph.g2d.G2DNode; import org.simantics.scenegraph.utils.DPIUtil; import org.simantics.scl.runtime.Lists; import org.simantics.scl.runtime.tuple.Tuple3; public class DistrictNetworkHoverInfoNode extends G2DNode implements HoverSensitiveNode { 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; private Point2D origin; private boolean hover = false; private static AtomicReference activeNode = new AtomicReference<>(); @Override public void render(Graphics2D g) { if (!hover || activeNode.get() != this) return; AffineTransform ot = g.getTransform(); Font of = g.getFont(); doRender(g); g.setFont(of); g.setTransform(ot); } private void doRender(Graphics2D g) { 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 delete() { super.delete(); activeNode.getAndUpdate(current -> current == this ? null : current); } }