X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=org.simantics.district.network.ui%2Fsrc%2Forg%2Fsimantics%2Fdistrict%2Fnetwork%2Fui%2Fnodes%2FDistrictNetworkHoverInfoNode.java;fp=org.simantics.district.network.ui%2Fsrc%2Forg%2Fsimantics%2Fdistrict%2Fnetwork%2Fui%2Fnodes%2FDistrictNetworkHoverInfoNode.java;h=24b7f5898c0dd0a06970ce1b28147c1b623120d2;hb=900f0e33431c1e3ea7d94d73cbf4d21f533dac26;hp=07ad2eedb4df1decd66f4ac0e965a76a67d6039c;hpb=320681ec7f81eccbdaf8b168ee001d0a43df31f1;p=simantics%2Fdistrict.git diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkHoverInfoNode.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkHoverInfoNode.java index 07ad2eed..24b7f589 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkHoverInfoNode.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkHoverInfoNode.java @@ -2,14 +2,16 @@ package org.simantics.district.network.ui.nodes; import java.awt.Color; import java.awt.Font; +import java.awt.FontMetrics; 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.Arrays; import java.util.List; -import java.util.Optional; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.ToIntFunction; import org.simantics.district.network.ui.styles.DistrictNetworkHoverInfoStyle; import org.simantics.maps.MapScalingTransform; @@ -21,138 +23,213 @@ 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.GeometryUtils; 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); + private static final long serialVersionUID = 1L; + + public static final String NODE_KEY = "DISTRICT_NETWORK_HOVER_INFO"; + + private static final int PAD = 15; + + private List labels; + + private Font font = new Font( + Font.SANS_SERIF, + Font.PLAIN, + (int)(DPIUtil.upscale(9) * MapScalingTransform.getScaleY() + 0.5)); + + @SuppressWarnings("unused") + private Point2D origin; + + private boolean hover = false; + + private Point2D mousePosition; + + /** + * For rendering and clipping the hover info base rectangle. + */ + private Rectangle2D bgRect = new Rectangle2D.Double(); + + 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; + if (labels.isEmpty() || mousePosition == null) + return; + AffineTransform ot = g.getTransform(); + Font of = g.getFont(); + doRender(g); + g.setFont(of); + g.setTransform(ot); + } + + private ToIntFunction widthMeasurer(FontMetrics fm) { + return s -> fm.stringWidth(s); + } + + private void doRender(Graphics2D g) { + AffineTransform tt = getTransform(); + g.transform(tt); + + g.translate(mousePosition.getX(), mousePosition.getY()); + //g.translate(origin.getX(), origin.getY()); + double scale = DPIUtil.upscale( 1.25 / GeometryUtils.getScale(g.getTransform()) ); + g.scale(scale, scale); + + g.setFont(font); + + FontMetrics fm = g.getFontMetrics(); + ToIntFunction sizer = widthMeasurer(fm); + + double rowHeight = fm.getHeight() * 1.1; + + // Let's calculate the max widths of each column. + // However, the last label is assumed to be a title row. + List values = labels.subList(0, labels.size() - 1); + Tuple3 title = labels.get(labels.size() - 1); + + int maxLabelWidth = values.stream().map(t -> (String) t.c0).mapToInt(sizer).max().orElse(PAD); + int[] valueWidths = values.stream().map(t -> (String) t.c1).mapToInt(sizer).toArray(); + int maxValueWidth = Arrays.stream(valueWidths).max().orElse(PAD); + int maxUnitWidth = values.stream().map(t -> (String) t.c2).mapToInt(sizer).max().orElse(PAD); + + String titleString = + (Objects.toString(title.c0, "") + + " " + Objects.toString(title.c1, "") + + " " + Objects.toString(title.c2, "")) + .trim(); + int titleWidth = sizer.applyAsInt(titleString); + + int titleRowWidth = PAD + titleWidth + PAD; + int maxValueRowWidth = PAD + maxLabelWidth + PAD + maxValueWidth + PAD + maxUnitWidth + PAD; + + int totalWidth = Math.max(maxValueRowWidth, titleRowWidth); + int totalHeight = (int) Math.round((rowHeight) * labels.size()); + + double minX = -(PAD + maxLabelWidth + PAD + maxValueWidth + PAD); + double minY = -(totalHeight + (int)Math.round(rowHeight)); + + bgRect.setRect(minX, minY, totalWidth, totalHeight + PAD); + confineIn(bgRect, g.getClip().getBounds2D()); + 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); - } + g.fill(bgRect); + + g.setColor(Color.DARK_GRAY); + g.draw(bgRect); + + g.setColor(Color.BLACK); + g.translate(bgRect.getMinX() - minX, bgRect.getMinY() - minY); + + int rows = values.size(); + double y = -rowHeight; + + float labelX = -(maxLabelWidth + PAD + maxValueWidth + PAD); + float unitX = 0; + + for (int i = 0; i < rows; ++i) { + Tuple3 t = values.get(i); + float ty = (float) y; + + if (t.c0 != null && ((String) t.c0).length() > 0) { + g.drawString((String) t.c0, labelX, ty); + } + if (t.c1 != null && ((String) t.c1).length() > 0) { + g.drawString((String) t.c1, -(valueWidths[i] + PAD), ty); + } + if (t.c2 != null && ((String) t.c2).length() > 0) { + g.drawString((String) t.c2, unitX, ty); + } + + y -= rowHeight; + } + + if (!titleString.trim().isEmpty()) { + int titleX = (int) (labelX - PAD + (bgRect.getWidth() - titleWidth) * 0.5); + g.drawString(titleString, titleX, (float) y); + } + } + + private Rectangle2D confineIn(Rectangle2D r, Rectangle2D bounds) { + // Handle right/bottom overrun + double maxX = Math.min(r.getMaxX(), bounds.getMaxX()); + double maxY = Math.min(r.getMaxY(), bounds.getMaxY()); + r.setFrame( + maxX - r.getWidth(), + maxY - r.getHeight(), + r.getWidth(), + r.getHeight()); + + // Handle left/top overrun + r.setFrame( + Math.max(r.getMinX(), bounds.getMinX()), + Math.max(r.getMinY(), bounds.getMinY()), + r.getWidth(), + r.getHeight()); + + return r; + } + + @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: " + "");