maint: added livewatch asset

This commit is contained in:
Chris
2025-08-31 18:14:07 -04:00
parent 7f5d95787b
commit ae2371a6fa
385 changed files with 150792 additions and 0 deletions

View File

@@ -0,0 +1,113 @@
using System;
using UnityEditor;
using UnityEngine;
namespace Ingvar.LiveWatch.Editor
{
public class CellSettingsDropdownWindow : EditorWindow
{
protected float ColumnWidth
{
get => WatchStorageSO.instance.ColumnWidth;
set => WatchStorageSO.instance.ColumnWidth = value;
}
protected float RowHeight
{
get => WatchStorageSO.instance.RowHeight;
set => WatchStorageSO.instance.RowHeight = value;
}
protected bool IsLeftSelection
{
get => WatchStorageSO.instance.IsLeftSelection;
set => WatchStorageSO.instance.IsLeftSelection = value;
}
private int selectionTypeIndex;
private static string[] selectionTypes = new[] {"Left", "Right"};
private static Vector2 windowSize = new Vector2(150, 90);
public static void Create(Rect buttonRect)
{
var window = CreateInstance<CellSettingsDropdownWindow>();
window.ShowAsDropDown(buttonRect, windowSize);
window.Focus();
}
private void OnGUI()
{
var prevWidth = ColumnWidth;
var prevHeight = RowHeight;
DrawBackground();
DrawWidthSlider();
DrawHeightSlider();
DrawSelectionPopup();
DrawRestoreDefaultsButton();
if (Math.Abs(prevWidth - ColumnWidth) > 0.001f
|| Math.Abs(prevHeight - RowHeight) > 0.001f)
{
var window = GetWindow<LiveWatchWindow>();
EditorUtility.SetDirty(window);
Focus();
}
}
private void DrawBackground()
{
if (Event.current.type != EventType.Repaint)
return;
var rect = new Rect(0, 0, position.width, position.height);
EditorGUI.DrawRect(rect, Color.black);
EditorGUI.DrawRect(rect.Extrude(ExtrudeFlags.All, -1), Colors.Background);
}
private void DrawWidthSlider()
{
EditorGUILayout.BeginHorizontal();
GUILayout.Label(new GUIContent("Width", "Width of watch cells"), GUILayout.Width(60));
ColumnWidth = GUILayout.HorizontalSlider(ColumnWidth, Constants.VariableValueColumnWidthMin, Constants.VariableValueColumnWidthMax);
GUILayout.Space(5);
EditorGUILayout.EndHorizontal();
}
private void DrawHeightSlider()
{
EditorGUILayout.BeginHorizontal();
GUILayout.Label(new GUIContent("Height", "Height of watch cells"), GUILayout.Width(60));
RowHeight = GUILayout.HorizontalSlider(RowHeight, Constants.VariableRowHeighMin, Constants.VariableRowHeightMax);
GUILayout.Space(5);
EditorGUILayout.EndHorizontal();
}
private void DrawSelectionPopup()
{
selectionTypeIndex = IsLeftSelection ? 0 : 1;
EditorGUILayout.BeginHorizontal();
GUILayout.Label(new GUIContent("Selection", "The direction of cell's selection"), GUILayout.Width(60));
selectionTypeIndex = EditorGUILayout.Popup(selectionTypeIndex, selectionTypes);
GUILayout.Space(5);
EditorGUILayout.EndHorizontal();
IsLeftSelection = selectionTypeIndex == 0;
}
private void DrawRestoreDefaultsButton()
{
GUILayout.FlexibleSpace();
if (GUILayout.Button(new GUIContent("Restore defaults", "Reset all view preferences to default values")))
{
ColumnWidth = 30;
RowHeight = 30;
IsLeftSelection = false;
}
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: b48c34bd3e504c09803af7a0b361e42b
timeCreated: 1724491075
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/Scripts/Editor/WatchVariablesGUI/CellSettingsDropdownWindow.cs
uploadId: 770587

View File

@@ -0,0 +1,100 @@
using System;
using System.Text;
using UnityEditor;
using UnityEngine;
namespace Ingvar.LiveWatch.Editor
{
[Serializable]
public class InfoGUI
{
public WatchVariable SelectedVariable { get; set; }
public bool IsTitleSelected { get; set; }
public int SelectedColumn { get; set; }
private Rect contentRect;
private Vector2 scrollPosition;
public void OnGUI(Rect areaRect)
{
EditorGUI.DrawRect(areaRect.Extrude(ExtrudeFlags.Top, 1), Colors.Background);
if (SelectedVariable == null)
{
return;
}
if (IsTitleSelected)
{
DrawForTitle(areaRect);
}
else if (SelectedVariable.HasValues)
{
DrawForValues(areaRect);
}
}
private void DrawForTitle(Rect rect)
{
contentRect = rect.Extrude(ExtrudeFlags.All, -2);
GUILayout.BeginArea(contentRect);
scrollPosition = GUILayout.BeginScrollView(scrollPosition);
DrawVariableName();
GUILayout.FlexibleSpace();
GUILayout.EndScrollView();
GUILayout.EndArea();
}
private void DrawForValues(Rect rect)
{
if (!SelectedVariable.Values.AnyAt(SelectedColumn))
{
return;
}
contentRect = rect.Extrude(ExtrudeFlags.All, -2);
GUILayout.BeginArea(contentRect);
scrollPosition = GUILayout.BeginScrollView(scrollPosition, GUIStyle.none);
DrawValueText();
GUILayout.EndScrollView();
GUILayout.EndArea();
}
private void DrawVariableName()
{
DrawLabel(SelectedVariable.Name, Styles.InfoValueText);
}
private void DrawValueText()
{
var selectedValueText = SelectedVariable.GetValueText(SelectedColumn);
if (string.IsNullOrWhiteSpace(selectedValueText))
{
return;
}
DrawLabel(selectedValueText, Styles.InfoValueText);
}
private void DrawLabel(string text, GUIStyle textStyle)
{
var content = WatchEditorServices.GUICache.GetContent(text);
var estimatedHeight = textStyle.CalcHeight(content, contentRect.width);
EditorGUILayout.SelectableLabel(
text,
textStyle,
GUILayout.ExpandWidth(true),
GUILayout.ExpandHeight(false),
GUILayout.Height(estimatedHeight));
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: ad9df77212b54b11b26b9e11a74868ba
timeCreated: 1657039461
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/Scripts/Editor/WatchVariablesGUI/InfoGUI.cs
uploadId: 770587

View File

@@ -0,0 +1,171 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Profiling;
namespace Ingvar.LiveWatch.Editor
{
public class LiveWatchWindow : EditorWindow
{
public static bool IsRepaintRequested { get; set; }
public static int SelectedColumnIndex { get; set; } = -1;
public static float MaxPlayRepaintDelay { get; set; } = 1f;
public static float MinSoftRepaintDelay { get; set; } = 0.3f;
private Vector2 _watchesScrollPosition;
[SerializeField] private ToolbarGUI _toolbarGUI = new ToolbarGUI();
[SerializeField] private VariablesListGUI _variablesListGUI = new VariablesListGUI();
[SerializeField] private InfoGUI _infoGUI = new InfoGUI();
[SerializeField] private ResizerGUI _horizontalResizerSearchToList = new ResizerGUI(true, 8, 1, Color.black);
[SerializeField] private ResizerGUI _horizontalResizerListToInfo = new ResizerGUI(true, 8, 1, Color.black, 1);
[SerializeField] private ResizerGUI _verticalResizerNameToValues = new ResizerGUI(false, 8, 1, Color.black);
private float previousRepaintTime;
[MenuItem("Window/LiveWatch")]
static void Init()
{
LiveWatchWindow window = (LiveWatchWindow)EditorWindow.GetWindow(typeof(LiveWatchWindow));
window.titleContent = new GUIContent("LiveWatch");
window.Show();
}
private void OnEnable()
{
IsRepaintRequested = false;
_variablesListGUI.OnEnable();
EditorApplication.update += OnEditorUpdate;
Watch.OnClearedAll += OnWatchClearedAll;
Watch.OnDestroyedAll += OnWatchDestroyedAll;
}
private void OnDisable()
{
EditorApplication.update -= OnEditorUpdate;
Watch.OnClearedAll -= OnWatchClearedAll;
Watch.OnDestroyedAll -= OnWatchDestroyedAll;
}
private void OnEditorUpdate()
{
if (EditorApplication.isPlaying)
{
if (Time.realtimeSinceStartup < previousRepaintTime + MaxPlayRepaintDelay)
return;
Repaint();
}
else if (IsRepaintRequested)
{
if (Time.realtimeSinceStartup < previousRepaintTime + MinSoftRepaintDelay)
return;
IsRepaintRequested = false;
Repaint();
}
}
private void OnGUI()
{
if (Event.current.type == EventType.Repaint)
previousRepaintTime = Time.realtimeSinceStartup;
Profiler.BeginSample("Watch GUI");
HandleResizers();
DrawVariableList();
DrawToolbar();
DrawInfo();
DrawResizers();
Profiler.EndSample();
}
private void OnWatchClearedAll()
{
_variablesListGUI.Clear();
}
private void OnWatchDestroyedAll()
{
_variablesListGUI.Clear();
}
private void HandleResizers()
{
if (_toolbarGUI.Search)
{
_horizontalResizerSearchToList.LocalArea = new Rect(0, 0, position.width, position.height)
.CropFromStartToPosition(CropEdge.TopLocal, _horizontalResizerListToInfo.Position)
.CropFromPositionToEnd(CropEdge.TopLocal, Constants.ToolbarAreaHeight + Constants.SearchAreaMinHeight)
.CropFromPositionToEnd(CropEdge.BottomLocal, Constants.VariablesAreaMinHeight);
_horizontalResizerSearchToList.ProcessHandle();
}
_verticalResizerNameToValues.LocalArea = new Rect(0, 0, position.width, position.height)
.CropFromStartToPosition(CropEdge.TopLocal, _horizontalResizerListToInfo.Position)
.CropFromPositionToEnd(CropEdge.TopLocal, _toolbarGUI.Search ? _horizontalResizerSearchToList.Position : Constants.ToolbarAreaHeight)
.CropFromPositionToEnd(CropEdge.LeftLocal, Constants.VariableLabelMinWidth)
.CropFromPositionToEnd(CropEdge.RightLocal, Constants.VariableValuesMinWidth);
_horizontalResizerListToInfo.LocalArea = new Rect(0, 0, position.width, position.height)
.CropFromPositionToEnd(CropEdge.TopLocal, Constants.ToolbarAreaHeight + Constants.SearchAreaMinHeight + Constants.VariablesAreaMinHeight)
.CropFromPositionToEnd(CropEdge.BottomLocal, Constants.InfoAreaMinHeight);
_verticalResizerNameToValues.ProcessHandle();
_horizontalResizerListToInfo.ProcessHandle();
}
private void DrawResizers()
{
if (_toolbarGUI.Search)
{
_horizontalResizerSearchToList.DrawLine();
}
_verticalResizerNameToValues.DrawLine();
_horizontalResizerListToInfo.DrawLine();
}
private void DrawToolbar()
{
var toolbarRect = new Rect(0, 0, position.width + 1, Constants.ToolbarAreaHeight);
_toolbarGUI.WindowRect = position;
_toolbarGUI.OnGUI(toolbarRect);
}
private void DrawVariableList()
{
var contentRect = new Rect(0, 0, position.width + 1, position.height)
.CropFromPositionToPosition(
CropEdge.TopLocal,
_toolbarGUI.Search ? _horizontalResizerSearchToList.Position : Constants.ToolbarAreaHeight,
_horizontalResizerListToInfo.Position);
_variablesListGUI.Search = _toolbarGUI.Search;
_variablesListGUI.NameColumnWidth = _verticalResizerNameToValues.Position;
_variablesListGUI.OnGUI(contentRect);
}
private void DrawInfo()
{
var infoRect = new Rect(0, _horizontalResizerListToInfo.Position + 1, position.width + 1, position.height - _horizontalResizerListToInfo.Position - 1);
_infoGUI.SelectedVariable = _variablesListGUI.SelectedVariable;
_infoGUI.IsTitleSelected = _variablesListGUI.IsTitleSelected;
_infoGUI.SelectedColumn = _variablesListGUI.SelectedColumnIndex;
_infoGUI.OnGUI(infoRect);
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 7f81180f981e90b459cd7620791b6aca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/Scripts/Editor/WatchVariablesGUI/LiveWatchWindow.cs
uploadId: 770587

View File

@@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Ingvar.LiveWatch.Editor
{
public class ToolbarGUI
{
public bool Search { get; set; }
public bool Live
{
get => Watch.IsLive;
set => Watch.IsLive = value;
}
public bool Collapse
{
get => WatchStorageSO.instance.Collapse;
set => WatchStorageSO.instance.Collapse = value;
}
public WatchStorage Watches
{
get => WatchStorageSO.instance.Watches;
set
{
Watch.DestroyAll();
WatchStorageSO.instance.Watches = value;
}
}
public Rect WindowRect { get; set; }
private Rect areaRect;
private float xOffsetRight;
private float xOffsetLeft;
private GUIContent saveButtonContent;
private GUIContent loadButtonContent;
private GUIContent optionsButtonContent;
private List<WatchVariable> variables = new();
public void OnGUI(Rect areaRect)
{
this.areaRect = areaRect;
GUI.Label(areaRect, string.Empty, EditorStyles.toolbar);
xOffsetLeft = 0;
var centerBlockWidth = Constants.ToolbarLiveButtonWidth + Constants.ToolbarCollapseButtonWidth + Constants.ToolbarClearButtonWidth;
xOffsetLeft = areaRect.width/2 - centerBlockWidth/2;
DoLiveButton(CropEdge.LeftLocal, ref xOffsetLeft);
DoCollapseButton(CropEdge.LeftLocal, ref xOffsetLeft);
DoClearButton(CropEdge.LeftLocal, ref xOffsetLeft);
xOffsetRight = 0;
DoOptionsButton(CropEdge.RightLocal, ref xOffsetRight);
DoViewSettingsButton(CropEdge.RightLocal, ref xOffsetRight);
}
private void DoOptionsButton(CropEdge edge, ref float offset)
{
optionsButtonContent ??= EditorGUIUtility.TrIconContent("_Menu", "Extra options");
var rect = areaRect.CropFromPositionWithSize(edge, offset, Constants.ToolbarOptionsButtonWidth);
if (GUI.Button(rect, optionsButtonContent, EditorStyles.toolbarButton))
{
GenericMenu genericMenu = new GenericMenu();
genericMenu.AddItem(new GUIContent(
"Preferences", "Go to Live Watch preferences"),
false,
new GenericMenu.MenuFunction(OpenPreferences));
genericMenu.AddItem(new GUIContent(
"Collapse all rows", "Collapses all watched variables recursively"),
false,
new GenericMenu.MenuFunction(CollapseAll));
genericMenu.AddItem(new GUIContent(
"Destroy all watches", "Directly calls Watch.DestroyAll() and removes all watched variables"),
false,
new GenericMenu.MenuFunction(Watch.DestroyAll));
genericMenu.DropDown(rect);
}
offset += Constants.ToolbarOptionsButtonWidth;
void OpenPreferences()
{
SettingsService.OpenUserPreferences("Preferences/Live Watch");
}
void CollapseAll()
{
Watches.GetAllChildRecursive(variables, WatchFilters.NoChilds, WatchFilters.None);
foreach (var variable in variables)
variable.EditorMeta.IsExpanded = false;
}
}
private void DoViewSettingsButton(CropEdge edge, ref float offset)
{
var rect = areaRect.CropFromPositionWithSize(edge, offset, Constants.ToolbarViewButtonWidth);
var content = new GUIContent("View");
if (GUI.Button(rect, content, EditorStyles.toolbarDropDown))
{
var buttonWorldRect = new Rect(rect.x + WindowRect.x, rect.yMax + WindowRect.y, rect.width, rect.height);
CellSettingsDropdownWindow.Create(buttonWorldRect);
}
offset += Constants.ToolbarViewButtonWidth;
}
private void DoClearButton(CropEdge edge, ref float offset)
{
var rect = areaRect.CropFromPositionWithSize(edge, offset, Constants.ToolbarClearButtonWidth);
var content = new GUIContent("Clear", "Clear all watch values");
if (GUI.Button(rect, content, EditorStyles.toolbarButton))
{
Watch.ClearAll();
}
offset += Constants.ToolbarClearButtonWidth;
}
private void DoCollapseButton(CropEdge edge, ref float offset)
{
var rect = areaRect.CropFromPositionWithSize(edge, offset, Constants.ToolbarCollapseButtonWidth);
var content = new GUIContent("Collapse", Collapse ? "Show columns without unique values" : "Hide columns without unique values");
Collapse = GUI.Toggle(rect, Collapse, content, EditorStyles.toolbarButton);
offset += Constants.ToolbarCollapseButtonWidth;
}
private void DoLiveButton(CropEdge edge, ref float offset)
{
var rect = areaRect.CropFromPositionWithSize(edge, offset, Constants.ToolbarLiveButtonWidth);
var content = new GUIContent("Live", Live ? "Watches are recording. Click to pause" : "Watches are not recording. Click to unpause");
Live = GUI.Toggle(rect, Live, content, EditorStyles.toolbarButton);
offset += Constants.ToolbarLiveButtonWidth;
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: e16d283d694d4d4aa55978ca311438b6
timeCreated: 1651572292
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/Scripts/Editor/WatchVariablesGUI/ToolbarGUI.cs
uploadId: 770587

View File

@@ -0,0 +1,247 @@
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));
}
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 3514d7c2cb624b31ad8e441dc64facfe
timeCreated: 1686599538
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/Scripts/Editor/WatchVariablesGUI/VariableCellGUI.cs
uploadId: 770587

View File

@@ -0,0 +1,396 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace Ingvar.LiveWatch.Editor
{
[Serializable]
public class VariableGUI
{
public int Index { get; set; }
public int IndentLevel { get; set; }
public float LabelAreaWidth { get; set; }
public float ValueColumnWidth { get; set; }
public float ValueRowHeight { get; set; }
public bool IsMouseDraggingOverValues { get; set; }
public float HorizontalScrollValue { get; set; }
public int SelectedColumnIndex { get; set; }
public bool Search { get; set; }
public bool Collapse { get; set; }
public Rect VariablesTotalArea { get; set; }
public int StartIndex { get; set; }
public float RectOffset { get; set; }
public List<int> IndicesToDisplay { get; set; }
public int IndicesCount { get; set; }
public bool IsSelected { get; set; }
public VariableSelectedFitInfo SelectedFitInfo { get; set; }
public bool PreferRightSelection { get; set; }
public double MinValue { get; set; }
public double MaxValue { get; set; }
public bool IsNarrowMode => ValueColumnWidth <= 12;
public VariableClickInfo ClickInfo => _clickInfo;
protected Color BackgroundColor => Index % 2 == 0 ? Colors.Background : Colors.BackgroundOdd;
protected VariableCellGUI CellGUI
{
get { return _cellGUI ??= new VariableCellGUI(); }
}
protected VariableGraphGUI GraphGUI
{
get { return _graphGUI ??= new VariableGraphGUI(); }
}
[SerializeField] private VariableCellGUI _cellGUI;
[SerializeField] private VariableGraphGUI _graphGUI;
private WatchVariable _variable;
private VariableClickInfo _clickInfo;
private List<int> _segmentIndices = new(100);
public void Draw(Rect rect, WatchVariable variable)
{
_variable = variable;
_clickInfo = new VariableClickInfo()
{
CurrentPositionIndex = -1
};
var valuesRect = rect.CropFromPositionToEnd(CropEdge.LeftLocal, LabelAreaWidth);
CellGUI.IsSelected = IsSelected;
CellGUI.Variable = variable;
CellGUI.SelectedColumnIndex = SelectedColumnIndex;
CellGUI.IsNarrowMode = IsNarrowMode;
CellGUI.ValueColumnWidth = ValueColumnWidth;
CellGUI.ValuesRect = valuesRect;
CellGUI.SelectedFitInfo = SelectedFitInfo;
CellGUI.PreferRightSelection = PreferRightSelection;
CellGUI.IndicesToDisplay = IndicesToDisplay;
ProcessRowEvents(rect);
DrawValuesBackground(valuesRect);
DrawValues(valuesRect);
var labelBackgroundRect = rect.CropFromPositionToPosition(CropEdge.LeftLocal, 0, LabelAreaWidth);
var labelRect = rect.CropFromPositionToPosition(CropEdge.LeftLocal,
Constants.VariableLabelOffset + Constants.VariableLabelIndentWidth * IndentLevel, LabelAreaWidth);
DrawLabelBackground(labelBackgroundRect);
ProcessLabelEvents(labelRect);
DrawLabelContent(labelRect);
DrawMinMaxForGraph(labelRect);
if (IsSelected && variable.HasValues)
{
GUIExtensions.DrawColorFrame(SelectedColumnIndex > 0 ? rect : labelBackgroundRect, Colors.CellSelectionLine, 3);
}
}
private void DrawLabelBackground(Rect rect)
{
EditorGUI.DrawRect(rect, BackgroundColor);
}
#region Full row
private void ProcessRowEvents(Rect rect)
{
if (!VariablesTotalArea.Contains(Event.current.mousePosition)
|| Event.current.type != EventType.MouseDown && GUIUtility.hotControl != 0)
return;
GUIUtility.hotControl = 0;
if (rect.Contains(Event.current.mousePosition))
{
if (Event.current.isMouse && Event.current.button == 0)
{
_clickInfo.IsMouse = true;
}
}
}
#endregion
#region Label zone
private void ProcessLabelEvents(Rect rect)
{
if (!VariablesTotalArea.Contains(Event.current.mousePosition) || IsMouseDraggingOverValues)
{
return;
}
if (rect.Contains(Event.current.mousePosition))
{
_clickInfo.CurrentPositionIndex = -1;
_clickInfo.IsOverTitleArea = true;
}
}
private void DrawLabelContent(Rect rect)
{
if (!_variable.HasChilds)
{
if (Event.current.type == EventType.Repaint)
{
DrawLabelWitchSearchResult(
rect,
_variable.Name,
_variable.EditorMeta.SearchResult.NameResult,
Styles.VariableLabel);
}
return;
}
if (Event.current.type == EventType.Layout || Event.current.type == EventType.Repaint || rect.Contains(Event.current.mousePosition))
{
_variable.EditorMeta.IsExpanded = EditorGUI.Foldout(
rect,
_variable.EditorMeta.IsExpanded,
string.Empty,
Styles.VariableFoldoutLabel);
if (Event.current.type == EventType.Used)
_clickInfo.IsOverTitleArea = false;
}
if (Event.current.type == EventType.Repaint)
{
DrawLabelWitchSearchResult(
rect.CropFromPositionToEnd(CropEdge.LeftLocal, 15),
_variable.Name,
_variable.EditorMeta.SearchResult.NameResult,
Styles.VariableLabel);
}
}
private void DrawMinMaxForGraph(Rect rect)
{
if (Event.current.type != EventType.Repaint
|| _variable.Values.Type is WatchValueType.String or WatchValueType.Bool or WatchValueType.NotSet
|| !IsNarrowMode
|| ValueRowHeight < 38)
{
return;
}
var maxValueRect = rect
.CropFromPositionWithSize(CropEdge.TopLocal, 2 , Constants.VariableGraphMinValueHeight)
.CropFromPositionToEnd(CropEdge.RightLocal, 2);
var minValueRect = rect
.CropFromPositionWithSize(CropEdge.BottomLocal, 2, Constants.VariableGraphMinValueHeight)
.CropFromPositionToEnd(CropEdge.RightLocal, 2);
GUI.Label(maxValueRect, Math.Round(MaxValue, _variable.RuntimeMeta.DecimalPlaces).ToString(), Styles.VariableGraphMaxLabel);
GUI.Label(minValueRect, Math.Round(MinValue, _variable.RuntimeMeta.DecimalPlaces).ToString(), Styles.VariableGraphMinLabel);
}
private void DrawLabelWitchSearchResult(Rect labelRect, string text, SearchQueryResult searchResult, GUIStyle style)
{
if (searchResult.IsPositive)
{
var charStartIndex = searchResult.IsWholeSelection ? 0 : searchResult.SelectionStartIndex;
var charEndIndex = searchResult.IsWholeSelection ? text.Length : searchResult.SelectionEndIndex;
style.DrawWithTextSelection(
labelRect,
WatchEditorServices.GUICache.GetContent(text),
-1,
charStartIndex,
charEndIndex);
}
else
{
style.Draw(labelRect, WatchEditorServices.GUICache.GetContent(text), 0);
}
}
#endregion
#region Values zone
private void DrawValuesBackground(Rect rect)
{
if (Event.current.type is not EventType.Repaint)
return;
EditorGUI.DrawRect(rect, BackgroundColor);
}
private void DrawValues(Rect rect)
{
if (IndicesToDisplay.Count > 0
&& (Event.current.type is EventType.MouseDown && VariablesTotalArea.Contains(Event.current.mousePosition)
|| IsMouseDraggingOverValues)
&& Event.current.isMouse
&& Event.current.button == 0
&& GUIUtility.hotControl == 0)
{
if (rect.Contains(Event.current.mousePosition))
{
var clickedValueIndex = (int)Mathf.Floor((Event.current.mousePosition.x - (rect.x + RectOffset)) / ValueColumnWidth);
clickedValueIndex = Mathf.Clamp(clickedValueIndex, 0, IndicesToDisplay.Count - 1);
_clickInfo.IsMouse = true;
_clickInfo.CurrentPositionIndex = IndicesToDisplay[clickedValueIndex];
_clickInfo.MouseButton = 0;
}
else
{
var selectedIndex = 0;
_clickInfo.CurrentPositionIndex = IndicesToDisplay[selectedIndex];
}
}
if (IndicesCount == 0 || Event.current.type != EventType.Repaint)
{
TryDrawSelectedCell();
return;
}
if (IsNarrowMode)
DrawValuesAsGraph();
else
DrawValuesAsCells();
TryDrawSelectedCell();
if (_variable.HasChilds && !_variable.EditorMeta.IsExpanded)
{
var previewRect = rect
.CropFromPositionWithSize(CropEdge.LeftLocal, RectOffset, IndicesCount * ValueColumnWidth)
.Extrude(ExtrudeFlags.Top | ExtrudeFlags.Bottom, -1);
var drawRect = IsSelected ? previewRect.Extrude(ExtrudeFlags.Top | ExtrudeFlags.Bottom, -3) : previewRect;
WatchEditorServices.PreviewDrawer.Search = Search;
WatchEditorServices.PreviewDrawer.DrawPreview(previewRect, drawRect, _variable, IndicesToDisplay, Mathf.CeilToInt(ValueColumnWidth));
}
void DrawValuesAsGraph()
{
GraphGUI.RowIndex = Index;
GraphGUI.Variable = _variable;
GraphGUI.IndicesToDisplay = IndicesToDisplay;
GraphGUI.StartIndex = StartIndex;
GraphGUI.IndicesCount = IndicesCount;
GraphGUI.MinValue = MinValue;
GraphGUI.MaxValue = MaxValue;
GraphGUI.ValueColumnWidth = ValueColumnWidth;
GraphGUI.BackgroundColor = BackgroundColor;
var graphRect = rect.CropFromPositionWithSize(CropEdge.LeftLocal, RectOffset, IndicesCount * ValueColumnWidth);
GraphGUI.DrawValues(graphRect);
}
void DrawValuesAsCells()
{
_segmentIndices.Clear();
var previousLocalIndex = 0;
var previousKeyIndex = _variable.Values.GetOriginalKey(IndicesToDisplay[0]);
for (var localIndex = 0; localIndex < IndicesCount; localIndex++)
{
var key = IndicesToDisplay[localIndex];
var keyIndex = _variable.Values.GetOriginalKey(key);
var isOriginal = localIndex != 0 && previousKeyIndex != keyIndex;
if (isOriginal)
{
previousKeyIndex = keyIndex;
DrawSegment(previousLocalIndex, localIndex - 1, _segmentIndices);
previousLocalIndex = localIndex;
_segmentIndices.Clear();
}
_segmentIndices.Add(key);
}
DrawSegment(previousLocalIndex, IndicesCount - 1, _segmentIndices);
}
void TryDrawSelectedCell()
{
var isAnySelected = IndicesToDisplay.Contains(SelectedColumnIndex);
if (!isAnySelected)
return;
var cellRect = rect.CropFromPositionWithSize(
CropEdge.LeftLocal,
RectOffset + IndicesToDisplay.IndexOf(SelectedColumnIndex) * ValueColumnWidth,
ValueColumnWidth);
if (!_variable.Values.IsEmptyAt(SelectedColumnIndex) && SelectedColumnIndex < _variable.Values.Count)
{
CellGUI.DrawSelectedCell(cellRect, _variable.GetValueText(SelectedColumnIndex), SelectedColumnIndex);
}
else
{
CellGUI.DrawSelectedCelLine(cellRect);
}
}
void DrawSegment(int startLocalIndex, int endLocalIndex, List<int> contentIndices)
{
var valueIndex = contentIndices[0];
var numberValue = _variable.GetValueNumber(valueIndex);
var segmentRect = rect.CropFromPositionWithSize(
CropEdge.LeftLocal,
RectOffset + startLocalIndex * ValueColumnWidth,
ValueColumnWidth * (endLocalIndex - startLocalIndex + 1));
var progress = 0f;
if (!double.IsNaN(numberValue))
{
progress = (float)((MaxValue - MinValue) < 0.000001
? 1
: (numberValue - MinValue) / (MaxValue - MinValue));
}
CellGUI.DrawValueCellSegment(
segmentRect,
_variable.GetValueText(valueIndex),
contentIndices,
(float)progress);
}
}
#endregion
}
public struct VariableClickInfo
{
public bool IsMouse;
public int MouseButton;
public int CurrentPositionIndex;
public bool IsOverTitleArea;
}
public class VariableSelectedFitInfo
{
public bool CanFitLeft = true;
public bool CanFitRight = true;
public void Reset()
{
CanFitLeft = true;
CanFitRight = true;
}
public void MergeWith(VariableSelectedFitInfo other)
{
CanFitLeft &= other.CanFitLeft;
CanFitRight &= other.CanFitRight;
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: de1be43f08224713a1e76d603eae4b19
timeCreated: 1637774053
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/Scripts/Editor/WatchVariablesGUI/VariableGUI.cs
uploadId: 770587

View File

@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.Profiling;
namespace Ingvar.LiveWatch.Editor
{
[Serializable]
public class VariableGraphGUI
{
public int RowIndex { get; set; }
public WatchVariable Variable { get; set; }
public List<int> IndicesToDisplay { get; set; } = new();
public int StartIndex { get; set; }
public int IndicesCount { get; set; }
public double MinValue { get; set; }
public double MaxValue { get; set; }
public float ValueColumnWidth { get; set; }
public Color BackgroundColor { get; set; }
[SerializeField] private TextureDrawGUI _textureDrawGUI = new();
private TextureDrawGUI.GraphPointInfo[] _graphPoints = new TextureDrawGUI.GraphPointInfo[1000];
public void DrawValues(Rect rect)
{
var isEmptyVariable = Variable.Values.Count == 0 || Variable.Values.OriginalKeys.Count == 1 && Variable.Values.IsEmptyAt(0);
if (isEmptyVariable )
{
return;
}
var graphRect = isEmptyVariable ? rect : rect.Extrude(ExtrudeFlags.Top | ExtrudeFlags.Bottom, -3);
var valueColumnWidthInt = Mathf.RoundToInt(ValueColumnWidth);
var graphPointsCount = 0;
foreach (var index in IndicesToDisplay)
{
var noValue = Variable.Values.IsEmptyAt(index);
var filColor = Colors.GraphFill;
var topLineColor = Colors.GraphLine;
var bottomLineColor = Colors.GraphFill;
var pixelHeight = Mathf.RoundToInt(graphRect.height);
if (!noValue)
{
var value = Variable.GetValueNumber(index);
var normValue = MaxValue - MinValue < 0.000001 ? 1 : (value - MinValue) / (MaxValue - MinValue);
pixelHeight = Mathf.RoundToInt(graphRect.height * (float)normValue);
}
{
bottomLineColor = Colors.ExtraTextLineGraph;
}
for (var i = 0; i < valueColumnWidthInt; i++)
{
var isDivider = ValueColumnWidth > 1 && index != IndicesToDisplay[0] && i == 0;
var point = new TextureDrawGUI.GraphPointInfo()
{
IsEmpty = noValue ,
WithLine = !noValue,
PixelHeight = pixelHeight,
TopLineColor = topLineColor,
BottomLineColor = bottomLineColor,
FillColor = isDivider ? filColor + new Color32(10, 10, 10, 10) : filColor
};
_graphPoints[graphPointsCount++] = point;
if (graphPointsCount == _graphPoints.Length)
Array.Resize(ref _graphPoints, _graphPoints.Length * 2);
}
}
_textureDrawGUI.Prepare(graphRect);
_textureDrawGUI.DrawTestGraph(
BackgroundColor,
_graphPoints,
graphPointsCount);
_textureDrawGUI.DrawResult();
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 02fc4c661d1a4a69865d58d6be233daa
timeCreated: 1715788313
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/Scripts/Editor/WatchVariablesGUI/VariableGraphGUI.cs
uploadId: 770587

View File

@@ -0,0 +1,679 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace Ingvar.LiveWatch.Editor
{
[Serializable]
public class VariablesListGUI
{
public const int MaxDrawDepth = 100;
public WatchStorage Variables => WatchStorageSO.instance.Watches;
public bool Search { get; set; }
public float NameColumnWidth { get; set; } = 50;
public bool Collapse
{
get => WatchStorageSO.instance.Collapse;
set => WatchStorageSO.instance.Collapse = value;
}
public float ValueColumnWidth
{
get => WatchStorageSO.instance.ColumnWidth;
set => WatchStorageSO.instance.ColumnWidth = value;
}
public float ValueRowHeight
{
get => WatchStorageSO.instance.RowHeight;
set => WatchStorageSO.instance.RowHeight = value;
}
public bool IsLeftSelection
{
get => WatchStorageSO.instance.IsLeftSelection;
set => WatchStorageSO.instance.IsLeftSelection = value;
}
public int SelectedColumnIndex
{
get => LiveWatchWindow.SelectedColumnIndex;
set => LiveWatchWindow.SelectedColumnIndex = value;
}
public WatchVariable SelectedVariable { get; private set; }
public bool IsMouseDraggingOverValues { get; set; }
public bool IsTitleSelected => _isSelectedTitle;
protected VariableGUI VariableGUI
{
get { return _variableGUI ??= new VariableGUI(); }
}
private SortedSet<int> _nonShrinkableColumns = new SortedSet<int>();
private List<int> _indicesToDisplay = new List<int>(100);
private int _valueStartIndex;
private float _valueRectOffset;
private ScrollbarGUI _horizontalScrollbar = new ScrollbarGUI(true);
private ScrollbarGUI _verticalScrollbar = new ScrollbarGUI(false);
private Rect _frame;
private Rect _variableListRect;
private Rect _currentSelectionRect;
private bool _isSelectedTitle;
private int _variableDrawnCounter;
private int _variableTotalCounter;
private int _valriableValuesMaxCount;
private VariableGUI _variableGUI;
private GUIStyle _horizontalScrollStyle;
private GUIStyle _verticalScrollStyle;
private VariableSelectedFitInfo _selectedFitInfo = new();
private List<WatchVariable> _variables = new (10);
public void OnEnable()
{
if (SelectedVariable != null)
{
Variables.GetAllChildRecursive(_variables, WatchFilters.None, WatchFilters.None);
var found = false;
foreach (var variable in _variables)
found |= variable == SelectedVariable;
if (!found)
{
SelectedVariable = null;
SelectedColumnIndex = -1;
}
}
if (Variables.Count == 0)
{
_horizontalScrollbar.IsStickingToLast = true;
}
}
public void OnGUI(Rect frame)
{
_frame = frame;
ProcessEvents(Event.current);
PrepareVariables();
DoList();
DoHorizontalScroll();
DoVerticalScroll();
}
public void Clear()
{
SelectedColumnIndex = -1;
SelectedVariable = null;
_valriableValuesMaxCount = 0;
_isSelectedTitle = true;
_nonShrinkableColumns.Clear();
_indicesToDisplay.Clear();
_horizontalScrollbar.ScrollValue = 0;
_horizontalScrollbar.IsStickingToLast = true;
}
private void PrepareVariables()
{
RefreshNonShrinkableColumns();
RefreshIndicesToDisplay();
}
private void DoList()
{
_variableDrawnCounter = 0;
_variableTotalCounter = 0;
var posY = _frame.y - _verticalScrollbar.ScrollValue;
posY = Mathf.FloorToInt(posY);
_variableListRect = _frame
.CropFromPositionToEnd(CropEdge.RightLocal, Constants.VerticalScrollWidth)
.CropFromPositionToEnd(CropEdge.BottomLocal, Constants.HorizontalScrollHeight);
VariableGUI.LabelAreaWidth = NameColumnWidth;
VariableGUI.ValueColumnWidth = ValueColumnWidth;
VariableGUI.ValueRowHeight = ValueRowHeight;
VariableGUI.HorizontalScrollValue = _horizontalScrollbar.ScrollValue;
VariableGUI.SelectedColumnIndex = SelectedColumnIndex;
VariableGUI.Search = Search;
VariableGUI.Collapse = Collapse;
VariableGUI.VariablesTotalArea = _variableListRect;
VariableGUI.IndicesToDisplay = _indicesToDisplay;
VariableGUI.StartIndex = _valueStartIndex;
VariableGUI.RectOffset = _valueRectOffset;
VariableGUI.IsMouseDraggingOverValues = IsMouseDraggingOverValues;
foreach (var variableName in Variables.SortedNames)
{
posY = DrawWatchVariableRowRecursive(posY, Variables.Get(variableName), 0);
}
if (SelectedVariable != null)
{
GUIExtensions.DrawColorFrame(_currentSelectionRect, Colors.CellSelectionLine, 3);
}
}
private void DoVerticalScroll()
{
if (Event.current.type == EventType.Repaint)
{
var placeholderRect = _frame
.CropFromStartToPosition(CropEdge.RightLocal, Constants.VerticalScrollWidth)
.CropFromPositionToEnd(CropEdge.BottomLocal, Constants.HorizontalScrollHeight);
EditorGUI.DrawRect(placeholderRect, Colors.Background);
_verticalScrollStyle ??= new GUIStyle(GUI.skin.verticalScrollbar);
_verticalScrollStyle.Draw(placeholderRect, false, false, false, false);
}
var scrollRect = _frame
.CropFromStartToPosition(CropEdge.RightLocal, Constants.VerticalScrollWidth)
.CropFromPositionToEnd(CropEdge.BottomLocal, Constants.HorizontalScrollHeight);
var variablesTotalHeight = GetVariablesTotalHeight();
_verticalScrollbar.Prepare(scrollRect, scrollRect.height, 0, variablesTotalHeight);
_verticalScrollbar.Draw();
}
private void DoHorizontalScroll()
{
if (Event.current.type == EventType.Repaint)
{
var placeholderRect = _frame.CropFromStartToPosition(CropEdge.BottomLocal, Constants.HorizontalScrollHeight);
EditorGUI.DrawRect(placeholderRect, Colors.Background);
_horizontalScrollStyle ??= new GUIStyle(GUI.skin.horizontalScrollbar);
_horizontalScrollStyle.Draw(placeholderRect, false, false, false, false);
}
var scrollRect = _frame
.CropFromStartToPosition(CropEdge.BottomLocal, Constants.HorizontalScrollHeight)
.CropFromPositionToEnd(CropEdge.LeftLocal, NameColumnWidth);
var variablesMaxWidth = GetVariablesMaxWidth();
_horizontalScrollbar.AllowStickToLast = true;
_horizontalScrollbar.Prepare(scrollRect, scrollRect.width-Constants.VerticalScrollWidth, 0, variablesMaxWidth);
_horizontalScrollbar.Draw();
}
private void ProcessEvents(Event e)
{
if (e.type == EventType.MouseUp && IsMouseDraggingOverValues)
{
e.Use();
IsMouseDraggingOverValues = false;
}
else if (e.type == EventType.MouseDown && _frame.Contains(e.mousePosition))
{
GUIUtility.hotControl = 0;
GUIUtility.keyboardControl = 0;
}
else if (e.modifiers.HasFlag(UserSettings.WidthZoomKeys) && e.isScrollWheel)
{
e.Use();
float delta = -e.delta.y;
var newColumnWidth = Mathf.Clamp(ValueColumnWidth + delta, Constants.VariableValueColumnWidthMin, Constants.VariableValueColumnWidthMax);
_horizontalScrollbar.ResizeRelativeToPointer(e.mousePosition.x, newColumnWidth/ValueColumnWidth);
ValueColumnWidth = newColumnWidth;
}
else if (e.modifiers.HasFlag(UserSettings.HeightZoomKeys) && e.isScrollWheel)
{
e.Use();
float delta = -e.delta.y;
var newRowHeight = Mathf.Clamp(ValueRowHeight + delta, Constants.VariableRowHeighMin, Constants.VariableRowHeightMax);
_verticalScrollbar.ResizeRelativeToPointer(e.mousePosition.y, newRowHeight/ValueRowHeight);
ValueRowHeight = newRowHeight;
}
else if (e.modifiers.HasFlag(UserSettings.ScrollValuesKeys) && e.isScrollWheel)
{
e.Use();
float delta = e.delta.y * Constants.MouseScrollbarMultiplier;
_horizontalScrollbar.ScrollValue += delta;
}
else if (e.isScrollWheel)
{
e.Use();
float delta = e.delta.y * Constants.MouseScrollbarMultiplier;
_verticalScrollbar.ScrollValue += delta;
}
else if (e.keyCode == UserSettings.FlipSelectionKey && e.type == EventType.KeyDown)
{
e.Use();
IsLeftSelection = !IsLeftSelection;
}
else if (e.keyCode == UserSettings.ExpandVariableKey && e.type == EventType.KeyDown)
{
e.Use();
if (SelectedVariable != null)
SelectedVariable.EditorMeta.IsExpanded = !SelectedVariable.EditorMeta.IsExpanded;
}
else if (e.keyCode == UserSettings.PreviousVariableKey && e.type == EventType.KeyDown)
{
e.Use();
MoveToVariable(-1);
}
else if (e.keyCode == UserSettings.NextVariableKey && e.type == EventType.KeyDown)
{
e.Use();
MoveToVariable(1);
}
else if (e.keyCode == UserSettings.PreviousValueKey && e.type == EventType.KeyDown)
{
e.Use();
MoveToVariable(0, -1);
}
else if (e.keyCode == UserSettings.NextValueKey && e.type == EventType.KeyDown)
{
e.Use();
MoveToVariable(0, 1);
}
}
private void RefreshNonShrinkableColumns()
{
_valriableValuesMaxCount = 0;
Variables.GetAllChildRecursive(_variables, WatchFilters.None, WatchFilters.NoValues);
foreach (var variable in _variables)
{
_valriableValuesMaxCount = Mathf.Max(variable.Values.Count, _valriableValuesMaxCount);
if (WatchServices.VariableCreator.IsAlwaysShrinkable(variable))
{
continue;
}
var currentIndexOfOriginalKey = variable.EditorMeta.LastNonShrinkableIndexOfKey;
var maxIndexOfOriginalKeys = variable.Values.OriginalKeys.Count - 1;
while (currentIndexOfOriginalKey < maxIndexOfOriginalKeys)
{
currentIndexOfOriginalKey++;
_nonShrinkableColumns.Add(variable.Values.OriginalKeys[currentIndexOfOriginalKey]);
}
variable.EditorMeta.LastNonShrinkableIndexOfKey = currentIndexOfOriginalKey;
}
}
private void RefreshIndicesToDisplay()
{
_valueStartIndex = Mathf.FloorToInt(_horizontalScrollbar.ScrollValue / ValueColumnWidth);
_valueRectOffset = _valueStartIndex * ValueColumnWidth - _horizontalScrollbar.ScrollValue;
_valueRectOffset = Mathf.FloorToInt(_valueRectOffset);
var valuesWidth = _frame.width - Constants.VerticalScrollWidth - NameColumnWidth;
var columnsCount = Mathf.FloorToInt((valuesWidth - _valueRectOffset) / ValueColumnWidth);
_indicesToDisplay.Clear();
if (Collapse)
{
var rawIndex = 0;
foreach (var column in _nonShrinkableColumns)
{
if (_indicesToDisplay.Count > columnsCount)
break;
if (rawIndex++ < _valueStartIndex)
continue;
_indicesToDisplay.Add(column);
}
}
else
{
for (var i = _valueStartIndex; i < _valriableValuesMaxCount; i++)
{
if (_indicesToDisplay.Count > columnsCount)
break;
_indicesToDisplay.Add(i);
}
}
}
private float DrawWatchVariableRowRecursive(float positionY, WatchVariable variable, int recursionDepth)
{
if (recursionDepth > MaxDrawDepth)
{
return positionY;
}
_variableTotalCounter++;
var listStartY = _frame.y;
var listEndY = listStartY + _verticalScrollbar.Size;
var positionFinishY = positionY + ValueRowHeight;
if (positionY >= listEndY)
{
return positionY;
}
bool draw = positionFinishY > listStartY;
var rect = new Rect(0, positionY, _frame.width - Constants.VerticalScrollWidth, ValueRowHeight);
if (draw)
{
_variableDrawnCounter++;
OnBeforeVariableDraw(variable, recursionDepth);
VariableGUI.Draw(rect, variable);
if (VariableGUI.ClickInfo.IsMouse)
{
OnVariableClicked(variable);
}
}
if (!variable.EditorMeta.IsExpanded)
{
if (SelectedVariable == variable)
_currentSelectionRect = rect;
return positionFinishY;
}
foreach (var childVariableName in variable.Childs.SortedNames)
{
positionFinishY = DrawWatchVariableRowRecursive(positionFinishY, variable.Childs.Get(childVariableName), recursionDepth + 1);
}
if (SelectedVariable == variable)
{
_currentSelectionRect = rect.SetHeight(positionFinishY - positionY);
}
return positionFinishY;
}
private void OnBeforeVariableDraw(WatchVariable variable, int recursionDepth)
{
CalcIndicesCount();
CalcMinMaxLocal();
VariableGUI.Index = _variableTotalCounter;
VariableGUI.IndentLevel = recursionDepth;
VariableGUI.IsSelected = variable == SelectedVariable;
VariableGUI.SelectedFitInfo = _selectedFitInfo;
VariableGUI.PreferRightSelection = !IsLeftSelection;
void CalcIndicesCount()
{
VariableGUI.IndicesCount = 0;
foreach (var index in _indicesToDisplay)
{
if (variable.Values.Count <= index)
break;
VariableGUI.IndicesCount++;
}
}
void CalcMinMaxLocal()
{
VariableGUI.MinValue = double.MaxValue;
VariableGUI.MaxValue = double.MinValue;
if (variable.Values.Type is WatchValueType.Bool)
{
VariableGUI.MinValue = 0;
VariableGUI.MaxValue = 1;
return;
}
var previousOriginalKeyIndex = -1;
for (int i = 0; i < VariableGUI.IndicesCount; i++)
{
var key = _indicesToDisplay[i];
var keyIndex = variable.Values.GetOriginalKey(key);
if (keyIndex != previousOriginalKeyIndex)
{
previousOriginalKeyIndex = key;
var isValid = variable.IsValidNumberValue(key, out var value);
if (!isValid)
continue;
if (value > VariableGUI.MaxValue)
VariableGUI.MaxValue = value;
if (value < VariableGUI.MinValue)
VariableGUI.MinValue = value;
}
}
VariableGUI.MaxValue = Math.Max(VariableGUI.MaxValue, VariableGUI.MinValue);
}
}
private void OnVariableClicked(WatchVariable variable)
{
SelectedVariable = variable;
_isSelectedTitle = VariableGUI.ClickInfo.IsOverTitleArea;
if (_isSelectedTitle)
SelectedColumnIndex = -1;
if (VariableGUI.ClickInfo.CurrentPositionIndex >= 0)
SelectedColumnIndex = VariableGUI.ClickInfo.CurrentPositionIndex;
if (Event.current.type == EventType.MouseDown && !_isSelectedTitle)
IsMouseDraggingOverValues = true;
_selectedFitInfo.Reset();
GUI.changed = true;
if (Event.current.type is not (EventType.Layout or EventType.Repaint))
Event.current.Use();
}
private void MoveToVariable(int variableOffset, int indexOffset = 0)
{
if (_variables.Count == 0)
return;
if (SelectedVariable == null)
{
SelectedVariable = _variables[0];
_isSelectedTitle = true;
JumpToSelectedVariable();
return;
}
else if (variableOffset == 0 && indexOffset == 0)
{
JumpToSelectedVariable();
return;
}
Variables.GetAllChildRecursive(_variables, WatchFilters.FoldedIfChilds, WatchFilters.None);
if (indexOffset == 0)
{
if (variableOffset < 0)
{
SelectedVariable = GetPrevious();
JumpToSelectedVariable();
}
else
{
SelectedVariable = GetNext();
JumpToSelectedVariable();
}
return;
}
var currentSelectionIndex = _isSelectedTitle ? -1 : SelectedColumnIndex;
currentSelectionIndex += indexOffset;
currentSelectionIndex = Mathf.Clamp(currentSelectionIndex, 0, SelectedVariable.Values.Count-1);
if (currentSelectionIndex < -1)
{
SelectedVariable = GetPrevious();
SelectedColumnIndex = -1;
if (SelectedVariable.Values.Count > 0)
SelectedColumnIndex = SelectedVariable.Values.Count - 1;
_isSelectedTitle = SelectedVariable.Values.Count <= 0;
JumpToSelectedVariable();
}
else if (currentSelectionIndex >= SelectedVariable.Values.Count)
{
SelectedVariable = GetNext();
SelectedColumnIndex = -1;
_isSelectedTitle = true;
JumpToSelectedVariable();
}
else
{
SelectedColumnIndex = currentSelectionIndex;
_isSelectedTitle = SelectedColumnIndex < 0;
JumpToSelectedVariable();
}
void JumpToSelectedVariable()
{
JumpToVariable(
SelectedVariable,
_isSelectedTitle ? -1 : SelectedColumnIndex,
variableOffset != 0,
indexOffset != 0);
}
WatchVariable GetPrevious()
{
WatchVariable previousVariable = null;
foreach (var variable in _variables)
{
if (variable == SelectedVariable)
return previousVariable ?? _variables[^1];
previousVariable = variable;
}
return null;
}
WatchVariable GetNext()
{
WatchVariable firstVariable = _variables[0];
WatchVariable currentVariable = null;
foreach (var nextVariable in _variables)
{
if (currentVariable == SelectedVariable)
return nextVariable;
currentVariable = nextVariable;
}
return firstVariable;
}
}
private void JumpToVariable(WatchVariable variable, int index, bool vertical, bool horizontal)
{
_horizontalScrollbar.IsStickingToLast = false;
if (vertical)
{
_verticalScrollbar.EndValue = GetVariablesTotalHeight();
var indexOfVisibleVariable = _variables.IndexOf(variable);
_verticalScrollbar.SetNormalizedPosition((float)indexOfVisibleVariable / _variables.Count);
}
SelectedVariable = variable;
SelectedColumnIndex = index;
if (horizontal && index >= 0)
{
if (Collapse && !_nonShrinkableColumns.Contains(index))
{
Collapse = false;
_horizontalScrollbar.EndValue = VariableGUI.ValueColumnWidth * _valriableValuesMaxCount;
}
var valuesCount = Collapse ? _nonShrinkableColumns.Count : _valriableValuesMaxCount;
var normPos = (float)index / valuesCount;
_horizontalScrollbar.SetNormalizedPosition(normPos);
}
}
private float GetVariablesTotalHeight()
{
Variables.GetAllChildRecursive(_variables, WatchFilters.FoldedIfChilds, WatchFilters.None);
return _variables.Count * ValueRowHeight;
}
private float GetVariablesMaxWidth()
{
if (Collapse)
{
return _nonShrinkableColumns.Count * VariableGUI.ValueColumnWidth;
}
var maxWidth = 0f;
Variables.GetAllChildRecursive(_variables, WatchFilters.FoldedIfChilds, WatchFilters.NoValues);
foreach (var variable in _variables)
{
maxWidth = Mathf.Max(maxWidth, variable.Values.Count * VariableGUI.ValueColumnWidth);
}
return maxWidth;
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: cf450bb6780140c79376f33a3729a2d1
timeCreated: 1636229945
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/Scripts/Editor/WatchVariablesGUI/VariablesListGUI.cs
uploadId: 770587