462 lines
14 KiB
C#
462 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
using Codice.CM.Client.Differences.Graphic;
|
|
|
|
using Unity.CodeEditor;
|
|
using Unity.CodeEditor.Rendering;
|
|
using Unity.PlasticSCM.Editor.UI;
|
|
using Unity.PlasticSCM.Editor.UI.UIElements;
|
|
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering.VirtualTexturing;
|
|
using UnityEngine.UIElements;
|
|
|
|
using XDiffGui;
|
|
using XDiffGui.Drawing;
|
|
|
|
namespace Unity.PlasticSCM.Editor.Diff.Text
|
|
{
|
|
internal abstract class ActionBar : VisualElement
|
|
{
|
|
internal ActionBar(
|
|
Unity.CodeEditor.TextEditor textEditor,
|
|
IActionBarClickListener listener)
|
|
{
|
|
mTextEditor = textEditor;
|
|
mClickListener = listener;
|
|
|
|
style.flexShrink = 0;
|
|
style.width = WIDTH;
|
|
style.overflow = Overflow.Hidden;
|
|
style.backgroundColor = TextEditorColors.Background;
|
|
|
|
mImguiContainer = new IMGUIContainer(OnGUI);
|
|
mImguiContainer.pickingMode = PickingMode.Ignore;
|
|
mImguiContainer.focusable = false;
|
|
mImguiContainer.style.position = Position.Absolute;
|
|
mImguiContainer.style.left = 0;
|
|
mImguiContainer.style.top = 0;
|
|
mImguiContainer.style.right = 0;
|
|
mImguiContainer.style.bottom = 0;
|
|
Add(mImguiContainer);
|
|
|
|
mTextEditor.TextArea.TextView.VisualLinesChanged += OnVisualLinesChanged;
|
|
|
|
RegisterCallback<PointerDownEvent>(OnPointerDown);
|
|
RegisterCallback<PointerUpEvent>(OnPointerUp);
|
|
RegisterCallback<PointerMoveEvent>(OnPointerMove);
|
|
RegisterCallback<PointerLeaveEvent>(OnPointerLeave);
|
|
}
|
|
|
|
internal void Dispose()
|
|
{
|
|
mTextEditor.TextArea.TextView.VisualLinesChanged -= OnVisualLinesChanged;
|
|
|
|
UnregisterCallback<PointerDownEvent>(OnPointerDown);
|
|
UnregisterCallback<PointerUpEvent>(OnPointerUp);
|
|
UnregisterCallback<PointerMoveEvent>(OnPointerMove);
|
|
UnregisterCallback<PointerLeaveEvent>(OnPointerLeave);
|
|
|
|
foreach (VisualElement overlay in mCursorOverlays)
|
|
overlay.RemoveFromHierarchy();
|
|
|
|
mCursorOverlays.Clear();
|
|
}
|
|
|
|
internal List<DiffAction> DiffActions
|
|
{
|
|
get { return mDiffActions; }
|
|
set
|
|
{
|
|
mDiffActions = value;
|
|
UpdateActions();
|
|
}
|
|
}
|
|
|
|
internal void UpdateActions()
|
|
{
|
|
if (mDiffActions == null)
|
|
return;
|
|
|
|
if (resolvedStyle.width == 0)
|
|
return;
|
|
|
|
mDiffAreas.Clear();
|
|
|
|
TextView textView = mTextEditor.TextArea.TextView;
|
|
if (!textView.VisualLinesValid)
|
|
return;
|
|
|
|
IReadOnlyList<VisualLine> visibleLines = textView.VisualLines;
|
|
if (visibleLines.Count == 0)
|
|
return;
|
|
|
|
int firstVisibleLine = visibleLines[0].FirstDocumentLine.LineNumber;
|
|
int lastVisibleLine = visibleLines[visibleLines.Count - 1]
|
|
.FirstDocumentLine.LineNumber;
|
|
|
|
foreach (DiffAction action in mDiffActions)
|
|
{
|
|
ColorTextRegion region = GetTextRegion(action);
|
|
|
|
if (!region.Visible)
|
|
continue;
|
|
|
|
if (!TextRegionChecker.IsRegionVisible(
|
|
region, firstVisibleLine - 1, lastVisibleLine - 1))
|
|
continue;
|
|
|
|
float topPosition = GetTopPosition(region.InitialLine + 1);
|
|
float bottomPosition = GetBottomPosition(region.EndLine);
|
|
|
|
float y = topPosition;
|
|
float width = resolvedStyle.width;
|
|
float height = bottomPosition - y;
|
|
|
|
Rect rectangle = new Rect(
|
|
GetButtonX(),
|
|
y - textView.VerticalOffset + 1.5f,
|
|
width,
|
|
Mathf.Max(0, height - 1));
|
|
|
|
ActionBarArea area = new ActionBarArea();
|
|
area.DiffIndex = action.DiffIndex;
|
|
area.DiffAction = action;
|
|
area.Bounds = rectangle;
|
|
|
|
if (action.ShowAction)
|
|
area.Button = GetDifferenceButton(rectangle);
|
|
|
|
mDiffAreas.Add(area);
|
|
}
|
|
|
|
mImguiContainer.MarkDirtyRepaint();
|
|
UpdateCursorOverlays();
|
|
}
|
|
|
|
void UpdateCursorOverlays()
|
|
{
|
|
foreach (VisualElement overlay in mCursorOverlays)
|
|
overlay.RemoveFromHierarchy();
|
|
|
|
mCursorOverlays.Clear();
|
|
|
|
foreach (ActionBarArea area in mDiffAreas)
|
|
{
|
|
if (area.Button == null)
|
|
continue;
|
|
|
|
Rect bounds = area.Button.Bounds;
|
|
|
|
VisualElement overlay = new VisualElement();
|
|
overlay.style.position = Position.Absolute;
|
|
overlay.style.left = bounds.x;
|
|
overlay.style.top = bounds.y;
|
|
overlay.style.width = bounds.width;
|
|
overlay.style.height = bounds.height;
|
|
overlay.focusable = false;
|
|
overlay.SetMouseCursor(MouseCursor.Link);
|
|
|
|
Add(overlay);
|
|
mCursorOverlays.Add(overlay);
|
|
}
|
|
}
|
|
|
|
void OnGUI()
|
|
{
|
|
if (Event.current.type != EventType.Repaint)
|
|
return;
|
|
|
|
TextBoxDrawingInfo textBoxDrawingInfo = mTextEditor.Tag as TextBoxDrawingInfo;
|
|
if (textBoxDrawingInfo != null)
|
|
{
|
|
TextEditorDrawing.DrawTextRegions(
|
|
resolvedStyle.width,
|
|
textBoxDrawingInfo,
|
|
mTextEditor);
|
|
}
|
|
|
|
Handles.BeginGUI();
|
|
PaintDifferenceButtons();
|
|
PaintSeparatorLine();
|
|
Handles.EndGUI();
|
|
}
|
|
|
|
protected abstract ColorTextRegion GetTextRegion(DiffAction action);
|
|
protected abstract Vector2 GetLineStartPoint();
|
|
protected abstract Vector2 GetLineEndPoint();
|
|
protected abstract AreaButton GetDifferenceButton(Rect rectangle);
|
|
protected abstract float GetButtonX();
|
|
|
|
void OnVisualLinesChanged(object sender, EventArgs e)
|
|
{
|
|
UpdateActions();
|
|
}
|
|
|
|
void OnPointerDown(PointerDownEvent e)
|
|
{
|
|
if (e.button != 0)
|
|
return;
|
|
|
|
UnClickAllButtons();
|
|
|
|
Vector2 localPos = e.localPosition;
|
|
ActionBarArea clickedArea = GetButtonAreaAt(localPos);
|
|
|
|
if (clickedArea != null && clickedArea.Button != null)
|
|
{
|
|
clickedArea.Button.IsClicked = true;
|
|
mClickedButtonArea = clickedArea;
|
|
this.CapturePointer(e.pointerId);
|
|
e.StopPropagation();
|
|
}
|
|
|
|
mImguiContainer.MarkDirtyRepaint();
|
|
}
|
|
|
|
void OnPointerUp(PointerUpEvent e)
|
|
{
|
|
if (this.HasPointerCapture(e.pointerId))
|
|
this.ReleasePointer(e.pointerId);
|
|
|
|
UnClickAllButtons();
|
|
|
|
Vector2 localPos = e.localPosition;
|
|
ActionBarArea area = GetButtonAreaAt(localPos);
|
|
|
|
ActionBarArea clickedArea = mClickedButtonArea;
|
|
mClickedButtonArea = null;
|
|
|
|
mImguiContainer.MarkDirtyRepaint();
|
|
|
|
if (area == null || area != clickedArea)
|
|
return;
|
|
|
|
e.StopPropagation();
|
|
OnAreaClick(area);
|
|
}
|
|
|
|
void OnPointerMove(PointerMoveEvent e)
|
|
{
|
|
UnHoverAllButtons();
|
|
|
|
Vector2 localPos = e.localPosition;
|
|
ActionBarArea hoverArea = GetButtonAreaAt(localPos);
|
|
|
|
if (hoverArea != null && hoverArea.Button != null)
|
|
hoverArea.Button.IsHovered = true;
|
|
|
|
mImguiContainer.MarkDirtyRepaint();
|
|
}
|
|
|
|
void OnPointerLeave(PointerLeaveEvent e)
|
|
{
|
|
UnHoverAllButtons();
|
|
|
|
if (mClickedButtonArea == null)
|
|
UnClickAllButtons();
|
|
|
|
mImguiContainer.MarkDirtyRepaint();
|
|
}
|
|
|
|
protected Rect GetAreaButtonRectangle(Rect diffRegionRectangle)
|
|
{
|
|
return new Rect(
|
|
diffRegionRectangle.x +
|
|
(diffRegionRectangle.width - BUTTON_SIZE) / 2,
|
|
diffRegionRectangle.y + BUTTON_MARGIN_TOP,
|
|
BUTTON_SIZE,
|
|
BUTTON_SIZE);
|
|
}
|
|
|
|
void OnAreaClick(ActionBarArea area)
|
|
{
|
|
if (area == null) return;
|
|
if (area.Button == null) return;
|
|
|
|
if (mClickListener == null)
|
|
return;
|
|
|
|
mClickListener.OnButtonClick(area.DiffAction, area.Button.ButtonAction);
|
|
}
|
|
|
|
void UnClickAllButtons()
|
|
{
|
|
foreach (ActionBarArea area in mDiffAreas)
|
|
{
|
|
if (area.Button == null)
|
|
continue;
|
|
|
|
area.Button.IsClicked = false;
|
|
}
|
|
}
|
|
|
|
void UnHoverAllButtons()
|
|
{
|
|
foreach (ActionBarArea area in mDiffAreas)
|
|
{
|
|
if (area.Button == null)
|
|
continue;
|
|
|
|
area.Button.IsHovered = false;
|
|
}
|
|
}
|
|
|
|
void PaintDifferenceButtons()
|
|
{
|
|
foreach (ActionBarArea area in mDiffAreas)
|
|
{
|
|
if (area.Button == null)
|
|
continue;
|
|
|
|
area.Button.Paint();
|
|
}
|
|
}
|
|
|
|
void PaintSeparatorLine()
|
|
{
|
|
Vector2 start = GetLineStartPoint();
|
|
Vector2 end = GetLineEndPoint();
|
|
|
|
float x = Mathf.Min(start.x, end.x);
|
|
float y = Mathf.Min(start.y, end.y);
|
|
float width = Mathf.Max(1, Mathf.Abs(end.x - start.x));
|
|
float height = Mathf.Max(1, Mathf.Abs(end.y - start.y));
|
|
|
|
EditorGUI.DrawRect(
|
|
new Rect(x, y, width, height),
|
|
UnityStyles.Colors.BarBorder);
|
|
}
|
|
|
|
ActionBarArea GetButtonAreaAt(Vector2 point)
|
|
{
|
|
foreach (ActionBarArea area in mDiffAreas)
|
|
{
|
|
if (area.Button == null)
|
|
continue;
|
|
|
|
if (area.Button.Bounds.Contains(point))
|
|
return area;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
float GetTopPosition(int line)
|
|
{
|
|
VisualLine visualLine = mTextEditor.TextArea.TextView.GetVisualLine(line);
|
|
if (visualLine == null)
|
|
return 0;
|
|
|
|
return visualLine.VisualTop;
|
|
}
|
|
|
|
float GetBottomPosition(int line)
|
|
{
|
|
VisualLine visualLine = mTextEditor.TextArea.TextView.GetVisualLine(line);
|
|
if (visualLine == null)
|
|
return mTextEditor.TextArea.TextView.Bounds.height +
|
|
mTextEditor.TextArea.TextView.VerticalOffset;
|
|
|
|
return visualLine.VisualTop + visualLine.Height;
|
|
}
|
|
|
|
protected List<ActionBarArea> mDiffAreas = new List<ActionBarArea>();
|
|
|
|
ActionBarArea mClickedButtonArea;
|
|
List<DiffAction> mDiffActions;
|
|
readonly List<VisualElement> mCursorOverlays = new List<VisualElement>();
|
|
|
|
readonly Unity.CodeEditor.TextEditor mTextEditor;
|
|
readonly IActionBarClickListener mClickListener;
|
|
readonly IMGUIContainer mImguiContainer;
|
|
|
|
const float WIDTH = 16;
|
|
const int BUTTON_SIZE = 11;
|
|
const int BUTTON_MARGIN_TOP = 2;
|
|
}
|
|
|
|
internal class ActionBarArea
|
|
{
|
|
internal DiffAction DiffAction { get; set; }
|
|
internal AreaButton Button { get; set; }
|
|
internal int DiffIndex { get; set; }
|
|
internal Rect Bounds { get; set; }
|
|
}
|
|
|
|
internal class AreaButton
|
|
{
|
|
internal DiffButtonActions ButtonAction { get; set; }
|
|
internal Rect Bounds { get; set; }
|
|
internal bool IsHovered { get; set; }
|
|
internal bool IsClicked { get; set; }
|
|
|
|
internal AreaButton(DiffButtonActions action)
|
|
{
|
|
ButtonAction = action;
|
|
}
|
|
|
|
internal void Paint()
|
|
{
|
|
Color color;
|
|
|
|
if (IsClicked)
|
|
color = UnityStyles.Colors.Diff.ActionBar.ButtonClicked;
|
|
else if (IsHovered)
|
|
color = UnityStyles.Colors.Diff.ActionBar.ButtonHovered;
|
|
else
|
|
color = UnityStyles.Colors.Diff.ActionBar.ButtonNormal;
|
|
|
|
float cx = Mathf.Round(Bounds.x + Bounds.width / 2f);
|
|
float cy = Mathf.Round(Bounds.y + Bounds.height / 2f);
|
|
|
|
if (IsClicked)
|
|
{
|
|
cx += 1;
|
|
cy += 1;
|
|
}
|
|
|
|
Color prevColor = Handles.color;
|
|
Handles.color = color;
|
|
|
|
if (ButtonAction == DiffButtonActions.Delete)
|
|
DrawCross(cx, cy);
|
|
else if (ButtonAction == DiffButtonActions.Restore)
|
|
DrawDoubleChevron(cx, cy);
|
|
|
|
Handles.color = prevColor;
|
|
}
|
|
|
|
static void DrawCross(float cx, float cy)
|
|
{
|
|
DrawLine(cx - 3, cy - 3, cx + 3, cy + 3);
|
|
DrawLine(cx + 3, cy - 3, cx - 3, cy + 3);
|
|
}
|
|
|
|
static void DrawDoubleChevron(float cx, float cy)
|
|
{
|
|
DrawChevron(cx - 4, cx - 1, cy, 3);
|
|
DrawChevron(cx, cx + 3, cy, 3);
|
|
}
|
|
|
|
static void DrawLine(float x1, float y1, float x2, float y2)
|
|
{
|
|
Handles.DrawAAPolyLine(THICKNESS,
|
|
new Vector3(x1, y1, 0),
|
|
new Vector3(x2, y2, 0));
|
|
}
|
|
|
|
static void DrawChevron(float left, float tip, float cy, float half)
|
|
{
|
|
Handles.DrawAAPolyLine(THICKNESS,
|
|
new Vector3(left, cy - half, 0),
|
|
new Vector3(tip, cy, 0),
|
|
new Vector3(left, cy + half, 0));
|
|
}
|
|
|
|
const float THICKNESS = 2f;
|
|
}
|
|
}
|