From c8dfe17cd67c667e03d1e610c481f5ebb127985d Mon Sep 17 00:00:00 2001 From: "ruX[Ruslan Zaharov]" <post4ruX@gmail.com> Date: Fri, 20 Dec 2013 13:58:18 +0400 Subject: [PATCH] Render new line char in legend items --- .../xeiam/xchart/internal/chartpart/Axis.java | 2 +- .../xchart/internal/chartpart/Legend.java | 246 ++++++++++-------- 2 files changed, 141 insertions(+), 107 deletions(-) diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java index fedda1fc..eea409ad 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java @@ -200,7 +200,7 @@ public class Axis implements ChartPart { double chartLegendWidth = 0; if (getChartPainter().getStyleManager().getLegendPosition() == LegendPosition.OutsideE) { - chartLegendWidth = getChartPainter().getChartLegend().getSizeHint()[0]; + chartLegendWidth = getChartPainter().getChartLegend().getSizeHint(g)[0]; } double width = diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java index f39c4172..33c17baa 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java @@ -15,15 +15,17 @@ */ package com.xeiam.xchart.internal.chartpart; -import java.awt.BasicStroke; -import java.awt.Graphics2D; -import java.awt.Shape; -import java.awt.font.FontRenderContext; -import java.awt.font.TextLayout; +import java.awt.*; +import java.awt.font.*; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; +import java.util.*; +import java.util.List; import com.xeiam.xchart.Series; +import com.xeiam.xchart.StyleManager; import com.xeiam.xchart.StyleManager.ChartType; import com.xeiam.xchart.internal.markers.Marker; @@ -35,15 +37,19 @@ public class Legend implements ChartPart { private static final int LEGEND_MARGIN = 6; private static final int BOX_SIZE = 20; - /** parent */ + /** + * parent + */ private final ChartPainter chartPainter; - /** the bounds */ + /** + * the bounds + */ private Rectangle2D bounds; /** * Constructor - * + * * @param chartPainter */ public Legend(ChartPainter chartPainter) { @@ -53,57 +59,65 @@ public class Legend implements ChartPart { /** * get the width of the chart legend - * + * + * @param g * @return */ - protected double[] getSizeHint() { + protected double[] getSizeHint(Graphics2D g) { + if (!chartPainter.getStyleManager().isLegendVisible()) { + return new double[]{0, 0}; + } - if (chartPainter.getStyleManager().isLegendVisible()) { + StyleManager styleManager = getChartPainter().getStyleManager(); + FontMetrics fontMetrics = g.getFontMetrics(chartPainter.getStyleManager().getLegendFont()); + boolean isBar = styleManager.getChartType() == ChartType.Bar; - // determine legend text content max width - double legendTextContentMaxWidth = 0; - double legendTextContentMaxHeight = 0; + // determine legend text content max width + double legendTextContentMaxWidth = 0; + double legendTextContentMaxHeight = 0; - for (Series series : chartPainter.getAxisPair().getSeriesMap().values()) { + // determine legend content height + double legendContentHeight = 0; - TextLayout textLayout = new TextLayout(series.getName(), chartPainter.getStyleManager().getLegendFont(), new FontRenderContext(null, true, false)); - Rectangle2D rectangle = textLayout.getBounds(); - // System.out.println(rectangle); - if (rectangle.getWidth() > legendTextContentMaxWidth) { - legendTextContentMaxWidth = rectangle.getWidth(); - } - if (rectangle.getHeight() > legendTextContentMaxHeight) { - legendTextContentMaxHeight = rectangle.getHeight(); - } + for (Series series : chartPainter.getAxisPair().getSeriesMap().values()) { + List<Map.Entry<String, Rectangle2D>> seriesBounds = getSeriesBounds(series, g); + double blockHeight = 0; + for (Map.Entry<String, Rectangle2D> entry : seriesBounds) { + blockHeight += entry.getValue().getHeight(); + legendTextContentMaxWidth = Math.max(legendTextContentMaxWidth, entry.getValue().getWidth()); } - // determine legend content height - double maxContentHeight = 0; - if (getChartPainter().getStyleManager().getChartType() != ChartType.Bar) { - maxContentHeight = Math.max(legendTextContentMaxHeight, Marker.SIZE); - } - else { - maxContentHeight = Math.max(legendTextContentMaxHeight, BOX_SIZE); - } - double legendContentHeight = - maxContentHeight * getChartPainter().getAxisPair().getSeriesMap().size() + chartPainter.getStyleManager().getLegendPadding() * (getChartPainter().getAxisPair().getSeriesMap().size() - 1); + blockHeight = Math.max(blockHeight, isBar ? BOX_SIZE : Marker.SIZE); - // determine legend content width - double legendContentWidth = 0; - if (getChartPainter().getStyleManager().getChartType() != ChartType.Bar) { - legendContentWidth = getChartPainter().getStyleManager().getLegendSeriesLineLength() + chartPainter.getStyleManager().getLegendPadding() + legendTextContentMaxWidth; - } - else { - legendContentWidth = BOX_SIZE + chartPainter.getStyleManager().getLegendPadding() + legendTextContentMaxWidth; - } - // Legend Box - double legendBoxWidth = legendContentWidth + 2 * chartPainter.getStyleManager().getLegendPadding(); - double legendBoxHeight = legendContentHeight + 2 * chartPainter.getStyleManager().getLegendPadding(); - return new double[] { legendBoxWidth, legendBoxHeight, maxContentHeight }; + legendTextContentMaxHeight = Math.max(legendTextContentMaxHeight, blockHeight); + legendContentHeight += blockHeight; } - else { - return new double[] { 0, 0, 0 }; + + // vertical padding between items + double paddingSize = isBar ? styleManager.getLegendPadding() : fontMetrics.getDescent(); + legendContentHeight += paddingSize * (chartPainter.getAxisPair().getSeriesMap().size() - 1); + + // determine legend content width + double legendContentWidth = 0; + if (!isBar) { + legendContentWidth = styleManager.getLegendSeriesLineLength() + styleManager.getLegendPadding() + legendTextContentMaxWidth; + } else { + legendContentWidth = BOX_SIZE + styleManager.getLegendPadding() + legendTextContentMaxWidth; } + // Legend Box + double legendBoxWidth = legendContentWidth + 2 * styleManager.getLegendPadding(); + double legendBoxHeight = legendContentHeight + 2 * styleManager.getLegendPadding(); + return new double[]{legendBoxWidth, legendBoxHeight}; + } + + private List<Map.Entry<String, Rectangle2D>> getSeriesBounds(Series series, Graphics2D g) { + String lines[] = series.getName().split("\\n"); + List<Map.Entry<String, Rectangle2D>> stringBounds = new ArrayList<Map.Entry<String, Rectangle2D>>(lines.length); + for (String line : lines) { + Rectangle2D bounds = g.getFontMetrics().getStringBounds(line, g); + stringBounds.add(new AbstractMap.SimpleEntry<String, Rectangle2D>(line, bounds)); + } + return stringBounds; } @Override @@ -112,18 +126,23 @@ public class Legend implements ChartPart { bounds = new Rectangle2D.Double(); g.setFont(chartPainter.getStyleManager().getLegendFont()); - if (chartPainter.getStyleManager().isLegendVisible()) { + StyleManager styleManager = getChartPainter().getStyleManager(); + + if (!styleManager.isLegendVisible()) return; + + final double[] sizeHint = getSizeHint(g); - double legendBoxWidth = getSizeHint()[0]; - double legendBoxHeight = getSizeHint()[1]; - double maxContentHeight = getSizeHint()[2]; + double legendBoxWidth = sizeHint[0]; + double legendBoxHeight = sizeHint[1]; - // legend draw position - double xOffset = 0; - double yOffset = 0; - switch (chartPainter.getStyleManager().getLegendPosition()) { + FontMetrics fontMetrics = g.getFontMetrics(styleManager.getLegendFont()); + + // legend draw position + double xOffset = 0; + double yOffset = 0; + switch (styleManager.getLegendPosition()) { case OutsideE: - xOffset = chartPainter.getWidth() - legendBoxWidth - chartPainter.getStyleManager().getChartPadding(); + xOffset = chartPainter.getWidth() - legendBoxWidth - styleManager.getChartPadding(); yOffset = chartPainter.getPlot().getBounds().getY() + (chartPainter.getPlot().getBounds().getHeight() - legendBoxHeight) / 2.0; break; case InsideNW: @@ -149,65 +168,80 @@ public class Legend implements ChartPart { default: break; - } - g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); - Shape rect = new Rectangle2D.Double(xOffset + 1, yOffset + 1, legendBoxWidth - 2, legendBoxHeight - 2); - g.setColor(chartPainter.getStyleManager().getLegendBackgroundColor()); - g.fill(rect); - g.setColor(chartPainter.getStyleManager().getLegendBorderColor()); - g.draw(rect); - - // Draw legend content inside legend box - double startx = xOffset + chartPainter.getStyleManager().getLegendPadding(); - double starty = yOffset + chartPainter.getStyleManager().getLegendPadding(); - - for (Series series : chartPainter.getAxisPair().getSeriesMap().values()) { - - if (getChartPainter().getStyleManager().getChartType() != ChartType.Bar) { - - // paint line - if (getChartPainter().getStyleManager().getChartType() != ChartType.Scatter && series.getStroke() != null) { - g.setColor(series.getStrokeColor()); - g.setStroke(series.getStroke()); - Shape line = new Line2D.Double(startx, starty + maxContentHeight / 2.0, startx + getChartPainter().getStyleManager().getLegendSeriesLineLength(), starty + maxContentHeight / 2.0); - g.draw(line); - } - - // paint marker - if (series.getMarker() != null) { - g.setColor(series.getMarkerColor()); - series.getMarker().paint(g, startx + getChartPainter().getStyleManager().getLegendSeriesLineLength() / 2.0, starty + maxContentHeight / 2.0); - } + } + + g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + Shape rect = new Rectangle2D.Double(xOffset + 1, yOffset + 1, legendBoxWidth - 2, legendBoxHeight - 2); + g.setColor(styleManager.getLegendBackgroundColor()); + g.fill(rect); + g.setColor(styleManager.getLegendBorderColor()); + g.draw(rect); + + // Draw legend content inside legend box + double startx = xOffset + styleManager.getLegendPadding(); + double starty = yOffset + styleManager.getLegendPadding(); + + + for (Series series : chartPainter.getAxisPair().getSeriesMap().values()) { + List<Map.Entry<String, Rectangle2D>> seriesBounds = getSeriesBounds(series, g); + float blockHeight = 0; + for (Map.Entry<String, Rectangle2D> entry : seriesBounds) + blockHeight += entry.getValue().getHeight(); + + if (styleManager.getChartType() != ChartType.Bar) { + // paint line + if (styleManager.getChartType() != ChartType.Scatter && series.getStroke() != null) { + g.setColor(series.getStrokeColor()); + g.setStroke(series.getStroke()); + Shape line = new Line2D.Double(startx, starty + blockHeight / 2.0, startx + styleManager.getLegendSeriesLineLength(), starty + blockHeight / 2.0); + g.draw(line); + } + + // paint marker + if (series.getMarker() != null) { + g.setColor(series.getMarkerColor()); + series.getMarker().paint(g, startx + styleManager.getLegendSeriesLineLength() / 2.0, starty + blockHeight / 2.0); } - else { - // paint little box - if (series.getStroke() != null) { - g.setColor(series.getStrokeColor()); - Shape rectSmall = new Rectangle2D.Double(startx, starty, BOX_SIZE, BOX_SIZE); - g.fill(rectSmall); - } + } else { + // paint little box + if (series.getStroke() != null) { + g.setColor(series.getStrokeColor()); + Shape rectSmall = new Rectangle2D.Double(startx, starty, BOX_SIZE, BOX_SIZE); + g.fill(rectSmall); } + } - // paint series name + // paint series name + g.setColor(chartPainter.getStyleManager().getChartFontColor()); - g.setColor(chartPainter.getStyleManager().getChartFontColor()); - TextLayout layout = new TextLayout(series.getName(), chartPainter.getStyleManager().getLegendFont(), new FontRenderContext(null, true, false)); - if (getChartPainter().getStyleManager().getChartType() != ChartType.Bar) { - layout.draw(g, (float) (startx + getChartPainter().getStyleManager().getLegendSeriesLineLength() + chartPainter.getStyleManager().getLegendPadding()), - (float) (starty + (maxContentHeight - 1 + layout.getBounds().getHeight()) / 2.0)); + float itemOffsetY = -fontMetrics.getDescent(); + if (styleManager.getChartType() != ChartType.Bar) { + final float x = (float) (startx + styleManager.getLegendSeriesLineLength() + styleManager.getLegendPadding()); + for (Map.Entry<String, Rectangle2D> entry : seriesBounds) { + g.drawString(entry.getKey(), x, (float) (starty + entry.getValue().getHeight()) + itemOffsetY); + itemOffsetY += entry.getValue().getHeight(); } - else { - layout.draw(g, (float) (startx + BOX_SIZE + chartPainter.getStyleManager().getLegendPadding()), (float) (starty + (maxContentHeight + layout.getBounds().getHeight()) / 2.0)); + itemOffsetY = (float) Math.max(itemOffsetY, Marker.SIZE); + starty += blockHeight + fontMetrics.getDescent(); + } else { + final float x = (float) (startx + BOX_SIZE + styleManager.getLegendPadding()); + for (Map.Entry<String, Rectangle2D> entry : seriesBounds) { + double height = entry.getValue().getHeight(); + double centerOffsetY = (Math.max(BOX_SIZE, height) - height) / 2.0; + g.drawString(entry.getKey(), x, (float)(starty + height + itemOffsetY + centerOffsetY)); + itemOffsetY += height; } - starty = starty + maxContentHeight + chartPainter.getStyleManager().getLegendPadding(); + starty += Math.max(BOX_SIZE, blockHeight) + styleManager.getLegendPadding(); } - // bounds - bounds = new Rectangle2D.Double(xOffset, yOffset, legendBoxWidth, legendBoxHeight); - // g.setColor(Color.blue); - // g.draw(bounds); } + // bounds + bounds = new Rectangle2D.Double(xOffset, yOffset, legendBoxWidth, legendBoxHeight); + +// g.setColor(Color.blue); +// g.draw(bounds); + } @Override -- GitLab