Files
project-reset/Assets/Plugins/LiveWatchLite/Scripts/Editor/WatchVariablesGUI/VariableCellGUI.cs
2025-08-31 18:14:07 -04:00

247 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Ingvar.LiveWatch.Editor
{
[Serializable]
public class VariableCellGUI
{
public bool IsSelected { get; set; }
public int RowIndex { get; set; }
public int SelectedColumnIndex { get; set; }
public bool IsNarrowMode { get; set; }
public float ValueColumnWidth { get; set; }
public WatchVariable Variable { get; set; }
public Rect ValuesRect { get; set; }
public VariableSelectedFitInfo SelectedFitInfo { get; set; }
public bool PreferRightSelection { get; set; }
public List<int> IndicesToDisplay { get; set; }
private VariableSelectedFitInfo _currentFitInfo = new();
private float currentSideOffset;
private bool isRightSelection => PreferRightSelection ? SelectedFitInfo.CanFitRight : !SelectedFitInfo.CanFitLeft;
public void DrawValueCellSegment(Rect rect, string value, List<int> contentIndices, float progress)
{
if (Event.current.type is not EventType.Repaint)
return;
currentSideOffset = 0;
if (value == string.Empty)
{
return;
}
var isSelected = contentIndices.Contains(SelectedColumnIndex);
EditorGUI.DrawRect(rect.Extrude(ExtrudeFlags.All, -1f), Colors.SegmentFrame);
EditorGUI.DrawRect(rect.Extrude(ExtrudeFlags.All, -2.5f), RowIndex % 2 == 0 ? Colors.Background : Colors.BackgroundOdd);
var innerRect = rect.Extrude(ExtrudeFlags.All, -4f);
currentSideOffset = 4;
var progressPixels = Mathf.FloorToInt(Mathf.Clamp(progress * innerRect.height, 2, innerRect.height));
var progressRect = innerRect.CropFromPositionWithSize(CropEdge.BottomLocal, 0.5f, progressPixels);
var progressColor = RowIndex % 2 == 0 ? Colors.SegmentFill : Colors.SegmentFillOdd;
DrawFormatCells(progressRect, contentIndices, progressColor);
DrawDividerCells(progressRect.Extrude(ExtrudeFlags.Left | ExtrudeFlags.Right, 4f), contentIndices);
if (isSelected)
return;
var estimatedSize = Styles.VariableValue.CalcSize(WatchEditorServices.GUICache.GetContent(value));
estimatedSize.x -= 4;
var valueRect = innerRect;
if (estimatedSize.x > valueRect.width)
{
var dotsRect = valueRect
.CropFromPositionWithSize(CropEdge.RightLocal, 0, 7)
.CropFromPositionWithSize(CropEdge.BottomLocal, valueRect.height/2f - 6, 3);
GUI.DrawTexture(dotsRect,
Textures.Dots);
valueRect = valueRect.Extrude(ExtrudeFlags.Right, -6);
}
if (valueRect.width > 5)
{
var style = estimatedSize.x > valueRect.width ? Styles.VariableValueLeft : Styles.VariableValue;
DrawLabelWitchSearchResult(valueRect, Variable.GetValueText(contentIndices[0]), contentIndices[0], style);
}
}
public void DrawSelectedCell(Rect rect, string value, int index)
{
if (Event.current.type is not (EventType.Repaint or EventType.Layout))
return;
var availableRect = rect.Extrude(ExtrudeFlags.All, -1);
var estimatedSize = Styles.VariableValueSelected.CalcSize(WatchEditorServices.GUICache.GetContent(value))
+ Vector2.right * 10;
var resultWidth = Mathf.Max(availableRect.width, estimatedSize.x);
if (Event.current.type is not EventType.Repaint)
{
var cellRectRight = availableRect.CropFromStartToPosition(CropEdge.LeftLocal, resultWidth);
_currentFitInfo.CanFitRight = cellRectRight.xMax <= ValuesRect.xMax;
var cellRectLeft = availableRect.CropFromStartToPosition(CropEdge.RightLocal, resultWidth);
_currentFitInfo.CanFitLeft = cellRectLeft.xMin >= ValuesRect.xMin;
SelectedFitInfo.MergeWith(_currentFitInfo);
return;
}
var edge = PreferRightSelection
? SelectedFitInfo.CanFitRight ? CropEdge.LeftLocal : CropEdge.RightLocal
: SelectedFitInfo.CanFitLeft ? CropEdge.RightLocal : CropEdge.LeftLocal;
var distToValuesEdge = edge == CropEdge.LeftLocal
? ValuesRect.xMax - availableRect.xMin
: availableRect.xMax - ValuesRect.xMin;
if (distToValuesEdge > 40)
resultWidth = Mathf.Min(resultWidth, distToValuesEdge - 5);
var cellBackRect = availableRect.CropFromStartToPosition(edge, resultWidth);
if (IsNarrowMode)
{
cellBackRect = cellBackRect.OffsetByX(Mathf.Max(rect.width, Constants.VariableSelectionLineMinWidth)
* (edge == CropEdge.LeftLocal ? 1 : - 1));
cellBackRect = cellBackRect.Extrude(
ExtrudeFlags.Top | ExtrudeFlags.Bottom,
-Mathf.Abs(cellBackRect.height - estimatedSize.y)/2f);
}
EditorGUI.DrawRect(cellBackRect, IsNarrowMode ? Colors.CellBackgroundSelectedGraph : Colors.CellBackgroundSelected);
DrawPreviewTriangles(cellBackRect);
DrawSelectedCelLine(rect);
var cellValueRect = cellBackRect;
var isTextFit = estimatedSize.x < cellValueRect.width;
var textStyle = isTextFit ? Styles.VariableValueSelected : Styles.VariableValueSelectedLeft;
cellValueRect = cellValueRect.CropFromPositionToEnd(CropEdge.LeftLocal, isTextFit ? 0 : 5);
DrawLabelWitchSearchResult(cellValueRect, value, index, textStyle);
}
public void DrawSelectedCelLine(Rect rect)
{
if (Event.current.type is not EventType.Repaint)
return;
var edge = PreferRightSelection
? SelectedFitInfo.CanFitRight ? CropEdge.LeftLocal : CropEdge.RightLocal
: SelectedFitInfo.CanFitLeft ? CropEdge.RightLocal : CropEdge.LeftLocal;
var lineRect = IsNarrowMode
? rect
: rect.CropFromPositionWithSize(edge, 1, 3);
if (IsNarrowMode && lineRect.width < Constants.VariableSelectionLineMinWidth)
{
lineRect = new Rect(
lineRect.center.x - Constants.VariableSelectionLineMinWidth / 2,
lineRect.y,
Constants.VariableSelectionLineMinWidth,
lineRect.height);
}
EditorGUI.DrawRect(lineRect, IsNarrowMode ? Colors.CellSelectionLineGraph : Colors.CellSelectionLine);
}
private void DrawLabelWitchSearchResult(Rect labelRect, string text, int index, GUIStyle style, bool forceFullSelection = false)
{
if (Event.current.type is not EventType.Repaint)
return;
if (labelRect.width > ValueColumnWidth)
labelRect = labelRect.FitInRect(ValuesRect);
style.Draw(labelRect, WatchEditorServices.GUICache.GetContent(text), -1);
}
private void DrawFormatCells(Rect rect, List<int> contentIndices, Color fallbackColor)
{
{
TryDrawFormatRect(rect
);
return;
}
void TryDrawFormatRect(Rect formatRect
)
{
if (fallbackColor != Color.clear)
EditorGUI.DrawRect(formatRect, fallbackColor);
}
}
private void DrawDividerCells(Rect rect, List<int> contentIndices)
{
WatchEditorServices.CellDividerDrawer.Draw(rect, Mathf.FloorToInt(ValueColumnWidth));
}
private Rect GetCellRect(int index, Rect rect, List<int> contentIndices)
{
var cellWidth = ValueColumnWidth;
if (index == 0)
cellWidth -= currentSideOffset;
if (index == contentIndices.Count - 1)
cellWidth -= currentSideOffset;
var cellRect = rect.CropFromPositionWithSize(
CropEdge.LeftLocal,
index * ValueColumnWidth + (index == 0 ? 0 : -currentSideOffset),
cellWidth);
return cellRect;
}
private void DrawPreviewTriangles(Rect rect)
{
var isSelectedNumberValid = Variable.IsValidNumberValue(SelectedColumnIndex, out var selectedNumberValue);
if (Variable.Values.IsOriginalAt(SelectedColumnIndex))
{
var prevToSelectedIndex = IndicesToDisplay.IndexOf(SelectedColumnIndex) - 1;
if (prevToSelectedIndex >= 0)
{
var isPrevNumberValid = Variable.IsValidNumberValue(IndicesToDisplay[prevToSelectedIndex], out var prevNumberValue);
var isTopAngle = isPrevNumberValid && prevNumberValue >= selectedNumberValue;
var isNextToSearchResult = false;
var triangleLeftRect = rect
.CropFromPositionWithSize(CropEdge.LeftLocal, IsNarrowMode ? 1 : (isRightSelection ? 3 : 0), 4)
.CropFromPositionWithSize(isTopAngle ? CropEdge.TopLocal : CropEdge.BottomLocal, IsSelected && !IsNarrowMode ? 3 : 0, 4);
GUI.DrawTexture(triangleLeftRect, isTopAngle
? (isNextToSearchResult ? Textures.BlueTriangleTopLeft : Textures.WhiteTriangleTopLeft)
: (isNextToSearchResult ? Textures.BlueTriangleBottomLeft : Textures.WhiteTriangleBottomLeft));
}
}
var nextToSelectedIndex = IndicesToDisplay.IndexOf(SelectedColumnIndex) + 1;
if (nextToSelectedIndex < IndicesToDisplay.Count && Variable.Values.IsOriginalAt(IndicesToDisplay[nextToSelectedIndex]))
{
var isNextNumberValid = Variable.IsValidNumberValue(IndicesToDisplay[nextToSelectedIndex], out var nextNumberValue);
var isTopAngle = isNextNumberValid && nextNumberValue >= selectedNumberValue;
var isNextToSearchResult = false;
var triangleRightRect = rect
.CropFromPositionWithSize(CropEdge.RightLocal, isRightSelection || IsNarrowMode ? 0 : 3, 4)
.CropFromPositionWithSize(isTopAngle ? CropEdge.TopLocal : CropEdge.BottomLocal, IsSelected && !IsNarrowMode ? 3 : 0, 4);
GUI.DrawTexture(triangleRightRect, isTopAngle
? (isNextToSearchResult ? Textures.BlueTriangleTopRight : Textures.WhiteTriangleTopRight)
: (isNextToSearchResult ? Textures.BlueTriangleBottomRight : Textures.WhiteTriangleBottomRight));
}
}
}
}