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 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 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 contentIndices, Color fallbackColor) { { TryDrawFormatRect(rect ); return; } void TryDrawFormatRect(Rect formatRect ) { if (fallbackColor != Color.clear) EditorGUI.DrawRect(formatRect, fallbackColor); } } private void DrawDividerCells(Rect rect, List contentIndices) { WatchEditorServices.CellDividerDrawer.Draw(rect, Mathf.FloorToInt(ValueColumnWidth)); } private Rect GetCellRect(int index, Rect rect, List 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)); } } } }