596 lines
18 KiB
C#
596 lines
18 KiB
C#
using UnityEngine;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
public class WMG_Legend : WMG_GUI_Functions {
|
|
|
|
public enum legendTypes {Bottom, Right};
|
|
|
|
// public properties
|
|
public bool hideLegend { get {return _hideLegend;}
|
|
set {
|
|
if (_hideLegend != value) {
|
|
_hideLegend = value;
|
|
setGraphCallback();
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public legendTypes legendType { get {return _legendType;}
|
|
set {
|
|
if (_legendType != value) {
|
|
_legendType = value;
|
|
setGraphCallback();
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public WMG_Enums.labelTypes labelType { get {return _labelType;}
|
|
set {
|
|
if (_labelType != value) {
|
|
_labelType = value;
|
|
setGraphCallback();
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public bool showBackground { get {return _showBackground;}
|
|
set {
|
|
if (_showBackground != value) {
|
|
_showBackground = value;
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public bool oppositeSideLegend { get {return _oppositeSideLegend;}
|
|
set {
|
|
if (_oppositeSideLegend != value) {
|
|
_oppositeSideLegend = value;
|
|
setGraphCallback();
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public float offset { get {return _offset;}
|
|
set {
|
|
if (_offset != value) {
|
|
_offset = value;
|
|
setGraphCallback();
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public float legendEntryWidth { get {return _legendEntryWidth;}
|
|
set {
|
|
if (_legendEntryWidth != value) {
|
|
_legendEntryWidth = value;
|
|
setGraphCallback();
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public bool setWidthFromLabels { get {return _setWidthFromLabels;}
|
|
set {
|
|
if (_setWidthFromLabels != value) {
|
|
_setWidthFromLabels = value;
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public float legendEntryHeight { get {return _legendEntryHeight;}
|
|
set {
|
|
if (_legendEntryHeight != value) {
|
|
_legendEntryHeight = value;
|
|
setGraphCallback();
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public int numRowsOrColumns { get {return _numRowsOrColumns;}
|
|
set {
|
|
if (_numRowsOrColumns != value) {
|
|
_numRowsOrColumns = value;
|
|
// setGraphCallback();
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public int numDecimals { get {return _numDecimals;}
|
|
set {
|
|
if (_numDecimals != value) {
|
|
_numDecimals = value;
|
|
setGraphCallback();
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public float legendEntryLinkSpacing { get {return _legendEntryLinkSpacing;}
|
|
set {
|
|
if (_legendEntryLinkSpacing != value) {
|
|
_legendEntryLinkSpacing = value;
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public int legendEntryFontSize { get {return _legendEntryFontSize;}
|
|
set {
|
|
if (_legendEntryFontSize != value) {
|
|
_legendEntryFontSize = value;
|
|
setGraphCallback();
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public float legendEntrySpacing { get {return _legendEntrySpacing;}
|
|
set {
|
|
if (_legendEntrySpacing != value) {
|
|
_legendEntrySpacing = value;
|
|
setGraphCallback();
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public float pieSwatchSize { get {return _pieSwatchSize;}
|
|
set {
|
|
if (_pieSwatchSize != value) {
|
|
_pieSwatchSize = value;
|
|
setGraphCallback();
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public float backgroundPadding { get {return _backgroundPadding;}
|
|
set {
|
|
if (_backgroundPadding != value) {
|
|
_backgroundPadding = value;
|
|
setGraphCallback();
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public bool autofitEnabled { get {return _autofitEnabled;}
|
|
set {
|
|
if (_autofitEnabled != value) {
|
|
_autofitEnabled = value;
|
|
setGraphCallback();
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public Color labelColor { get {return _labelColor;}
|
|
set {
|
|
if (_labelColor != value) {
|
|
_labelColor = value;
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public FontStyle legendEntryFontStyle { get {return _legendEntryFontStyle;}
|
|
set {
|
|
if (_legendEntryFontStyle != value) {
|
|
_legendEntryFontStyle = value;
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
public Font legendEntryFont { get {return _legendEntryFont;}
|
|
set {
|
|
if (_legendEntryFont != value) {
|
|
_legendEntryFont = value;
|
|
legendC.Changed();
|
|
}
|
|
}
|
|
}
|
|
|
|
public WMG_Graph_Manager theGraph;
|
|
public GameObject background;
|
|
public GameObject entriesParent;
|
|
public Object emptyPrefab;
|
|
public List<WMG_Legend_Entry> legendEntries;
|
|
|
|
private WMG_Pie_Graph pieGraph;
|
|
private WMG_Axis_Graph axisGraph;
|
|
|
|
// Private backing variables
|
|
[SerializeField] private bool _hideLegend;
|
|
[SerializeField] private legendTypes _legendType;
|
|
[SerializeField] private WMG_Enums.labelTypes _labelType;
|
|
[SerializeField] private bool _showBackground;
|
|
[SerializeField] private bool _oppositeSideLegend;
|
|
[SerializeField] private float _offset;
|
|
[SerializeField] private float _legendEntryWidth;
|
|
[SerializeField] private bool _setWidthFromLabels;
|
|
[SerializeField] private float _legendEntryHeight;
|
|
[SerializeField] private int _numRowsOrColumns;
|
|
[SerializeField] private int _numDecimals;
|
|
[SerializeField] private float _legendEntryLinkSpacing;
|
|
[SerializeField] private int _legendEntryFontSize;
|
|
[SerializeField] private float _legendEntrySpacing;
|
|
[SerializeField] private float _pieSwatchSize;
|
|
[SerializeField] private float _backgroundPadding;
|
|
[SerializeField] private bool _autofitEnabled;
|
|
[SerializeField] private Color _labelColor;
|
|
[SerializeField] private FontStyle _legendEntryFontStyle = FontStyle.Normal;
|
|
[SerializeField] private Font _legendEntryFont;
|
|
|
|
// Useful property getters
|
|
public int LegendWidth {
|
|
get {
|
|
return Mathf.RoundToInt(2*backgroundPadding + legendEntryLinkSpacing + legendEntryWidth *
|
|
(legendType == legendTypes.Right ? numRowsOrColumns : MaxInRowOrColumn));
|
|
}
|
|
}
|
|
|
|
public int LegendHeight {
|
|
get {
|
|
return Mathf.RoundToInt(2*backgroundPadding + legendEntryHeight *
|
|
(legendType == legendTypes.Bottom ? numRowsOrColumns : MaxInRowOrColumn));
|
|
}
|
|
}
|
|
|
|
public int NumEntries {
|
|
get {
|
|
int numEntries = legendEntries.Count;
|
|
for (int j = 0; j < legendEntries.Count; j++) {
|
|
if (!activeInHierarchy(legendEntries[j].gameObject)) numEntries--;
|
|
}
|
|
return numEntries;
|
|
}
|
|
}
|
|
|
|
public int MaxInRowOrColumn {
|
|
get {
|
|
return Mathf.CeilToInt(1f * NumEntries / numRowsOrColumns);
|
|
}
|
|
}
|
|
|
|
|
|
// Original property values for use with dynamic resizing
|
|
public float origLegendEntryWidth { get; private set; }
|
|
public float origLegendEntryHeight { get; private set; }
|
|
public float origLegendEntryLinkSpacing { get; private set; }
|
|
public int origLegendEntryFontSize { get; private set; }
|
|
public float origLegendEntrySpacing { get; private set; }
|
|
public float origPieSwatchSize { get; private set; }
|
|
public float origOffset { get; private set; }
|
|
public float origBackgroundPadding { get; private set; }
|
|
|
|
private bool hasInit;
|
|
|
|
private List<WMG_Change_Obj> changeObjs = new List<WMG_Change_Obj>();
|
|
public WMG_Change_Obj legendC = new WMG_Change_Obj();
|
|
|
|
public void Init() {
|
|
if (hasInit) return;
|
|
hasInit = true;
|
|
|
|
pieGraph = theGraph.GetComponent<WMG_Pie_Graph>();
|
|
axisGraph = theGraph.GetComponent<WMG_Axis_Graph>();
|
|
|
|
changeObjs.Add(legendC);
|
|
|
|
setOriginalPropertyValues();
|
|
|
|
legendC.OnChange += LegendChanged;
|
|
|
|
PauseCallbacks();
|
|
}
|
|
|
|
public void PauseCallbacks() {
|
|
for (int i = 0; i < changeObjs.Count; i++) {
|
|
changeObjs[i].changesPaused = true;
|
|
changeObjs[i].changePaused = false;
|
|
}
|
|
}
|
|
|
|
public void ResumeCallbacks() {
|
|
for (int i = 0; i < changeObjs.Count; i++) {
|
|
changeObjs[i].changesPaused = false;
|
|
if (changeObjs[i].changePaused) changeObjs[i].Changed();
|
|
}
|
|
}
|
|
|
|
public void LegendChanged() {
|
|
updateLegend();
|
|
}
|
|
|
|
void setGraphCallback() {
|
|
if (pieGraph != null) {
|
|
pieGraph.graphC.Changed();
|
|
}
|
|
}
|
|
|
|
public void setOriginalPropertyValues() {
|
|
origLegendEntryWidth = legendEntryWidth;
|
|
origLegendEntryHeight = legendEntryHeight;
|
|
origLegendEntryLinkSpacing = legendEntryLinkSpacing;
|
|
origLegendEntryFontSize = legendEntryFontSize;
|
|
origLegendEntrySpacing = legendEntrySpacing;
|
|
origPieSwatchSize = pieSwatchSize;
|
|
origOffset = offset;
|
|
origBackgroundPadding = backgroundPadding;
|
|
}
|
|
|
|
// Used for pie graphs
|
|
public WMG_Legend_Entry createLegendEntry(Object prefab) {
|
|
GameObject obj = Instantiate(prefab) as GameObject;
|
|
theGraph.changeSpriteParent(obj, entriesParent);
|
|
WMG_Legend_Entry entry = obj.GetComponent<WMG_Legend_Entry>();
|
|
entry.legend = this;
|
|
legendEntries.Add(entry);
|
|
return entry;
|
|
}
|
|
|
|
// Used for other graphs
|
|
public WMG_Legend_Entry createLegendEntry(Object prefab, WMG_Series series, int index) {
|
|
GameObject obj = Instantiate(prefab) as GameObject;
|
|
theGraph.changeSpriteParent(obj, entriesParent);
|
|
WMG_Legend_Entry entry = obj.GetComponent<WMG_Legend_Entry>();
|
|
entry.seriesRef = series;
|
|
entry.legend = this;
|
|
entry.nodeLeft = theGraph.CreateNode(emptyPrefab, obj);
|
|
entry.nodeRight = theGraph.CreateNode(emptyPrefab, obj);
|
|
// _legendEntrySpacing = theGraph.getSpritePositionX(entry.label);
|
|
legendEntries.Insert(index, entry);
|
|
return entry;
|
|
}
|
|
|
|
public void deleteLegendEntry(int index) {
|
|
DestroyImmediate(legendEntries[index].gameObject);
|
|
legendEntries.RemoveAt(index);
|
|
}
|
|
|
|
bool backgroundEnabled() {
|
|
int numSeries = 1;
|
|
if (axisGraph != null) {
|
|
numSeries = axisGraph.lineSeries.Count;
|
|
}
|
|
if (pieGraph != null) {
|
|
numSeries = pieGraph.sliceValues.Count;
|
|
}
|
|
if (!hideLegend && showBackground && numSeries != 0) return true;
|
|
return false;
|
|
}
|
|
|
|
float getMaxLabelWidth() {
|
|
float maxLabelWidth = 0;
|
|
foreach (WMG_Legend_Entry entry in legendEntries) {
|
|
float labelWidth = getTextSize(entry.label).x * entry.label.transform.localScale.x;
|
|
if (labelWidth > maxLabelWidth) maxLabelWidth = labelWidth;
|
|
}
|
|
return maxLabelWidth;
|
|
}
|
|
|
|
public void updateLegend() {
|
|
if (backgroundEnabled() && !theGraph.activeInHierarchy(background)) theGraph.SetActive(background,true);
|
|
if (!backgroundEnabled() && theGraph.activeInHierarchy(background)) theGraph.SetActive(background,false);
|
|
if (!hideLegend && !theGraph.activeInHierarchy(entriesParent)) theGraph.SetActive(entriesParent,true);
|
|
if (hideLegend && theGraph.activeInHierarchy(entriesParent)) theGraph.SetActive(entriesParent,false);
|
|
if (hideLegend) return;
|
|
|
|
float maxPointSize = 0;
|
|
|
|
Vector2 entriesAnchor = Vector2.zero;
|
|
Vector2 entriesPivot = Vector2.zero;
|
|
Vector2 entriesOffset = Vector2.zero;
|
|
|
|
if (axisGraph != null) {
|
|
maxPointSize = axisGraph.getMaxPointSize();
|
|
}
|
|
if (pieGraph != null) {
|
|
maxPointSize = pieSwatchSize;
|
|
}
|
|
|
|
if (legendType == legendTypes.Bottom) {
|
|
if (oppositeSideLegend) {
|
|
entriesAnchor = new Vector2 (0.5f, 1);
|
|
entriesPivot = entriesAnchor;
|
|
entriesOffset = new Vector2 (0, -offset);
|
|
}
|
|
else {
|
|
entriesAnchor = new Vector2 (0.5f, 0);
|
|
entriesPivot = entriesAnchor;
|
|
entriesOffset = new Vector2 (0, offset);
|
|
}
|
|
}
|
|
else if (legendType == legendTypes.Right) {
|
|
if (oppositeSideLegend) {
|
|
entriesAnchor = new Vector2 (0, 0.5f);
|
|
entriesPivot = entriesAnchor;
|
|
entriesOffset = new Vector2 (offset, 0);
|
|
}
|
|
else {
|
|
entriesAnchor = new Vector2 (1, 0.5f);
|
|
entriesPivot = entriesAnchor;
|
|
entriesOffset = new Vector2 (-offset, 0);
|
|
}
|
|
}
|
|
|
|
// For pie graphs anchor the legend to the edge of the pie
|
|
if (pieGraph != null) {
|
|
entriesOffset = new Vector2(-1 * entriesOffset.x, -1 * entriesOffset.y);
|
|
if (legendType == legendTypes.Bottom) {
|
|
entriesPivot = new Vector2(entriesPivot.x, 1 - entriesPivot.y);
|
|
}
|
|
else {
|
|
entriesPivot = new Vector2(1 - entriesPivot.x, entriesPivot.y);
|
|
}
|
|
}
|
|
|
|
changeSpriteWidth(this.gameObject, LegendWidth);
|
|
changeSpriteHeight(this.gameObject, LegendHeight);
|
|
|
|
setAnchor(this.gameObject, entriesAnchor, entriesPivot, entriesOffset);
|
|
|
|
Vector2 entriesParentOffset = new Vector2 (legendEntryLinkSpacing + backgroundPadding + maxPointSize/2f,
|
|
-legendEntryHeight/2f + LegendHeight/2f - backgroundPadding);
|
|
|
|
setAnchor(entriesParent, new Vector2 (0, 0.5f), new Vector2 (0, 0.5f), entriesParentOffset);
|
|
|
|
|
|
|
|
int numEntries = NumEntries;
|
|
int maxInRowOrColumn = MaxInRowOrColumn; // Max elements in a row for horizontal legends
|
|
|
|
if (numRowsOrColumns < 1) _numRowsOrColumns = 1; // Ensure not less than 1
|
|
if (numRowsOrColumns > numEntries) _numRowsOrColumns = numEntries; // Ensure cannot exceed number series
|
|
|
|
int extras = 0;
|
|
if (numEntries > 0) {
|
|
extras = numEntries % numRowsOrColumns; // When the number series does not divide evenly by the num rows setting, then this is the number of extras
|
|
}
|
|
int origExtras = extras; // Save the original extras, since we will need to decrement extras in the loop
|
|
int cumulativeOffset = 0; // Used to offset the other dimension, for example, elements moved to a lower row (y), need to also move certain distance (x) left
|
|
int previousI = 0; // Used to determine when the i (row for horizontal) has changed from the previous i, which is used to increment the cumulative offset
|
|
bool useSmaller = false; // Used to determine whether we need to subtract 1 from maxInRowOrColumn when calculating the cumulative offset
|
|
|
|
if (maxInRowOrColumn == 0) return; // Legend hidden / all entries deactivated
|
|
|
|
// Calculate the position of the legend entry for each line series
|
|
for (int j = 0; j < legendEntries.Count; j++) {
|
|
WMG_Legend_Entry legendEntry = legendEntries[j];
|
|
|
|
if (axisGraph != null) {
|
|
|
|
if (legendEntry.swatchNode == null) {
|
|
foreach(GameObject seriesGO in axisGraph.lineSeries) {
|
|
seriesGO.GetComponent<WMG_Series>().CreateOrDeleteSpritesBasedOnPointValues();
|
|
}
|
|
}
|
|
|
|
theGraph.changeSpritePositionRelativeToObjBy(legendEntry.nodeLeft, legendEntry.swatchNode, new Vector3(-legendEntryLinkSpacing, 0, 0));
|
|
theGraph.changeSpritePositionRelativeToObjBy(legendEntry.nodeRight, legendEntry.swatchNode, new Vector3(legendEntryLinkSpacing, 0, 0));
|
|
|
|
WMG_Link theLine = legendEntry.line.GetComponent<WMG_Link>();
|
|
theLine.Reposition();
|
|
}
|
|
else {
|
|
changeSpriteWidth(legendEntry.swatchNode, Mathf.RoundToInt(pieSwatchSize));
|
|
changeSpriteHeight(legendEntry.swatchNode, Mathf.RoundToInt(pieSwatchSize));
|
|
}
|
|
|
|
if (axisGraph != null) {
|
|
theGraph.changeSpritePositionToX(legendEntry.label, legendEntrySpacing);
|
|
}
|
|
else {
|
|
theGraph.changeSpritePositionToX(legendEntry.label, legendEntrySpacing + pieSwatchSize/2);
|
|
}
|
|
|
|
// Legend text
|
|
if (axisGraph != null) {
|
|
string theText = legendEntry.seriesRef.seriesName;
|
|
|
|
if (labelType == WMG_Enums.labelTypes.None) {
|
|
theText = "";
|
|
}
|
|
changeLabelText(legendEntry.label, theText);
|
|
}
|
|
changeLabelFontSize(legendEntry.label, legendEntryFontSize);
|
|
changeSpriteColor(legendEntry.label, labelColor);
|
|
// Font Style
|
|
changeLabelFontStyle(legendEntry.label, legendEntryFontStyle);
|
|
// Font
|
|
if (legendEntryFont != null) {
|
|
changeLabelFont(legendEntry.label, legendEntryFont);
|
|
}
|
|
|
|
// i is the row for horizontal legends, and the column for vertical
|
|
int i = Mathf.FloorToInt(j / maxInRowOrColumn);
|
|
if (origExtras > 0) {
|
|
i = Mathf.FloorToInt((j + 1) / maxInRowOrColumn);
|
|
}
|
|
|
|
// If there were extras, but no longer any more extras, then need to subtract 1 from the maxInRowOrColumn, and recalculate i
|
|
if (extras == 0 && origExtras > 0) {
|
|
i = origExtras + Mathf.FloorToInt((j - origExtras * maxInRowOrColumn)/ (maxInRowOrColumn - 1));
|
|
if ((j - origExtras * maxInRowOrColumn) > 0) useSmaller = true;
|
|
}
|
|
|
|
// When there are extras decrease i for the last element in the row
|
|
if (extras > 0) {
|
|
if ((j + 1) % maxInRowOrColumn == 0) {
|
|
extras--;
|
|
i--;
|
|
}
|
|
}
|
|
|
|
// Increment cumulative offset when i changes, use offset to position other dimension correctly.
|
|
if (previousI != i) {
|
|
previousI = i;
|
|
if (useSmaller) {
|
|
cumulativeOffset += (maxInRowOrColumn - 1);
|
|
}
|
|
else {
|
|
cumulativeOffset += maxInRowOrColumn;
|
|
}
|
|
}
|
|
|
|
// Set the position based on the series index (j), i (row index for horizontal), and cumulative offset
|
|
if (legendType == legendTypes.Bottom) {
|
|
theGraph.changeSpritePositionTo(legendEntry.gameObject, new Vector3(j * legendEntryWidth - legendEntryWidth * cumulativeOffset, -i * legendEntryHeight, 0));
|
|
}
|
|
else if (legendType == legendTypes.Right) {
|
|
theGraph.changeSpritePositionTo(legendEntry.gameObject, new Vector3(i * legendEntryWidth, -j * legendEntryHeight + legendEntryHeight * cumulativeOffset, 0));
|
|
}
|
|
}
|
|
|
|
// This needs to be called after label text is set
|
|
if (setWidthFromLabels) {
|
|
if (axisGraph != null && (axisGraph.graphType == WMG_Axis_Graph.graphTypes.line || axisGraph.graphType == WMG_Axis_Graph.graphTypes.line_stacked)) {
|
|
legendEntryWidth = Mathf.Max(legendEntryLinkSpacing, maxPointSize/2) + legendEntrySpacing + getMaxLabelWidth() + 5;
|
|
}
|
|
else {
|
|
legendEntryWidth = maxPointSize + legendEntrySpacing + getMaxLabelWidth() + 5;
|
|
}
|
|
}
|
|
|
|
if (autofitEnabled) {
|
|
if (legendType == legendTypes.Bottom) {
|
|
if (LegendWidth > getSpriteWidth(theGraph.gameObject)) {
|
|
if (numRowsOrColumns < NumEntries) {
|
|
numRowsOrColumns++;
|
|
}
|
|
}
|
|
else {
|
|
if (numRowsOrColumns > 1) {
|
|
_numRowsOrColumns--; // temporarily decrease without callback
|
|
if (LegendWidth > getSpriteWidth(theGraph.gameObject)) { // if new temporary width exceeds border, then dont do anything
|
|
_numRowsOrColumns++;
|
|
}
|
|
else { // new width doesn't exceed the border, actually decrease
|
|
_numRowsOrColumns++;
|
|
numRowsOrColumns--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (LegendHeight > getSpriteHeight(theGraph.gameObject)) {
|
|
if (numRowsOrColumns < NumEntries) {
|
|
numRowsOrColumns++;
|
|
}
|
|
}
|
|
else {
|
|
if (numRowsOrColumns > 1) {
|
|
_numRowsOrColumns--; // temporarily decrease without callback
|
|
if (LegendHeight > getSpriteHeight(theGraph.gameObject)) { // if new temporary width exceeds border, then dont do anything
|
|
_numRowsOrColumns++;
|
|
}
|
|
else { // new width doesn't exceed the border, actually decrease
|
|
_numRowsOrColumns++;
|
|
numRowsOrColumns--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setLabelScales(float newScale) {
|
|
foreach (WMG_Legend_Entry entry in legendEntries) {
|
|
entry.label.transform.localScale = new Vector3(newScale, newScale, 1);
|
|
}
|
|
}
|
|
|
|
}
|