Adaug proiectul Unity

This commit is contained in:
2026-11-01 23:39:55 +02:00
parent cb14d9d37d
commit 863eac8f2c
23463 changed files with 5412601 additions and 0 deletions
@@ -0,0 +1,52 @@
using UnityEngine;
using UnityEngine.Rendering;
namespace Unity.PlasticSCM.Editor.Diff.Texture
{
internal static class DrawTexture
{
internal static void WithColorMask(
Rect position,
UnityEngine.Texture image,
ScaleMode scaleMode,
ColorWriteMask colorWriteMask)
{
UnityEditor.EditorGUI.DrawTextureTransparent(
position,
image,
scaleMode,
0,
-1,
colorWriteMask | ColorWriteMask.Alpha);
}
internal static void DrawWithColorMaskAndOpacity(
Rect rect,
UnityEngine.Texture texture,
ScaleMode scaleMode,
ColorWriteMask mode,
float opacity)
{
Color prevColor = GUI.color;
try
{
Color tint = ToTintColor(mode);
GUI.color = new Color(tint.r, tint.g, tint.b, opacity);
GUI.DrawTexture(rect, texture, scaleMode);
}
finally
{
GUI.color = prevColor;
}
}
static Color ToTintColor(ColorWriteMask mode)
{
float r = (mode & ColorWriteMask.Red) != 0 ? 1f : 0f;
float g = (mode & ColorWriteMask.Green) != 0 ? 1f : 0f;
float b = (mode & ColorWriteMask.Blue) != 0 ? 1f : 0f;
return new Color(r, g, b, 1f);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d404d462a8e74ea1a55c9f4af8b1f625
timeCreated: 1774869485
@@ -0,0 +1,8 @@
namespace Unity.PlasticSCM.Editor.Diff.Texture
{
internal interface IImageDiffToolbar
{
void EnableDifferencesMode();
void DisableDifferencesMode();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9887c425b1adc4e3895315e3eec339ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,229 @@
using System;
using Codice.Client.Common.Threading;
using Codice.CM.Client.Differences;
using Codice.CM.Client.Differences.Graphic;
using MergetoolGui;
using PlasticGui;
using Unity.PlasticSCM.Editor.Diff.Text;
using Unity.PlasticSCM.Editor.Diff.Purged;
using Unity.PlasticSCM.Editor.Diff.Texture.Toolbar;
using Unity.PlasticSCM.Editor.UI.UIElements;
using UnityEngine.Rendering;
using UnityEngine.UIElements;
using XDiffGui;
namespace Unity.PlasticSCM.Editor.Diff.Texture
{
internal class ImageContentControl : VisualElement, IBigFilePanelListener
{
internal ImageContentControl(
IBigFileDownloader bigFileDownloader,
IBigFileChecker bigFileChecker)
{
mBigFileDownloader = bigFileDownloader;
mBigFileChecker = bigFileChecker;
CreateGUI();
}
internal void ShowData(DiffViewerData data)
{
mDiffViewerData = data;
ReplaceMainView(mNormalContentPanel);
if (BigFileDiffCalculator.IsBigFileDiff(data, mBigFileDownloader, mBigFileChecker))
{
ShowBigFileMessage();
return;
}
if (data.Left?.IsPurged == true || data.Right?.IsPurged == true)
{
ShowPurgedRevision(data);
return;
}
mMessagePanel.HandleMessage(data.Message);
mFileTextView.text = PlasticLocalization.GetString(
PlasticLocalization.Name.Content, data.Left.SymbolicName);
ShowTexture(data.Left.File);
}
internal void Dispose()
{
if (mBigFileMessagePanel != null)
mBigFileMessagePanel.Dispose();
if (mPurgedRevisionControl != null)
mPurgedRevisionControl.Dispose();
mChannelOptionsView.Dispose();
mImageViewPanel.Dispose();
}
void IBigFilePanelListener.OnCalculateDifferencesButtonClick()
{
if (mBigFileDownloader == null)
{
ShowTexture(mDiffViewerData.Left.File);
return;
}
mBigFileMessagePanel.Disable();
IThreadWaiter waiter = ThreadWaiter.GetWaiter(10);
waiter.Execute(
/*threadOperationDelegate*/ delegate
{
mBigFileDownloader.DownloadFiles(mDiffViewerData, null);
},
/*afterOperationDelegate*/ delegate
{
mBigFileMessagePanel.Enable();
if (waiter.Exception != null)
{
MergetoolExceptionsHandler.DisplayException(
waiter.Exception);
return;
}
if (mDiffViewerData.Left?.IsPurged == true ||
mDiffViewerData.Right?.IsPurged == true)
{
ShowPurgedRevision(mDiffViewerData);
return;
}
ShowTexture(mDiffViewerData.Left.File);
});
}
void ShowTexture(string file)
{
try
{
mImageViewPanel.ShowImage(file);
ReplaceView(mImageViewPanel);
}
catch (Exception ex)
{
mMessagePanel.HandleMessage(ex.Message);
mImageViewPanel.ClearImage();
}
}
void ShowBigFileMessage()
{
if (mBigFileMessagePanel == null)
mBigFileMessagePanel = new BigFileMessagePanel(this, false);
mBigFileMessagePanel.UpdateDisplayData(
BigFileDisplayData.Build(
mDiffViewerData, mBigFileDownloader));
mMessagePanel.HandleMessage(string.Empty);
ReplaceView(mBigFileMessagePanel);
}
void ShowPurgedRevision(DiffViewerData data)
{
if (mPurgedRevisionControl == null)
mPurgedRevisionControl = new PurgedRevisionControl();
mPurgedRevisionControl.ShowData(data);
ReplaceMainView(mPurgedRevisionControl);
}
void ReplaceMainView(VisualElement panel)
{
if (hierarchy.childCount == 1 && hierarchy[0] == panel)
return;
Clear();
Add(panel);
}
void ReplaceView(VisualElement view)
{
if (mCurrentView == view)
return;
if (mCurrentView != null)
mContainerPanel.Remove(mCurrentView);
mContainerPanel.Add(view);
mCurrentView = view;
}
void CreateGUI()
{
mMessagePanel = new MessagePanel();
VisualElement toolBarPanel = BuildToolBarPanel();
mContainerPanel = new VisualElement();
mContainerPanel.style.flexGrow = 1;
style.flexGrow = 1;
mNormalContentPanel = new VisualElement();
mNormalContentPanel.style.flexGrow = 1;
mNormalContentPanel.Add(mMessagePanel);
mNormalContentPanel.Add(toolBarPanel);
mNormalContentPanel.Add(mContainerPanel);
Add(mNormalContentPanel);
mImageViewPanel = new ImageViewPanel();
ReplaceView(mImageViewPanel);
}
VisualElement BuildToolBarPanel()
{
UnityEditor.UIElements.Toolbar toolbar = ControlBuilder.Toolbar.Create();
mFileTextView = ControlBuilder.Label.CreateSelectableLabel();
mFileTextView.style.unityTextAlign = UnityEngine.TextAnchor.MiddleLeft;
mFileTextView.style.overflow = Overflow.Hidden;
mFileTextView.style.textOverflow = TextOverflow.Ellipsis;
mFileTextView.style.flexGrow = 1;
mFileTextView.style.flexShrink = 1;
toolbar.Add(mFileTextView);
mChannelOptionsView = new ChannelOptionsView();
mChannelOptionsView.ChannelModeChanged += OnChannelModeChanged;
toolbar.Add(mChannelOptionsView);
return toolbar;
}
void OnChannelModeChanged(ColorWriteMask mode)
{
mImageViewPanel.SetChannelMode(mode);
}
VisualElement mNormalContentPanel;
VisualElement mContainerPanel;
VisualElement mCurrentView;
MessagePanel mMessagePanel;
PurgedRevisionControl mPurgedRevisionControl;
ImageViewPanel mImageViewPanel;
ChannelOptionsView mChannelOptionsView;
Label mFileTextView;
BigFileMessagePanel mBigFileMessagePanel;
DiffViewerData mDiffViewerData;
readonly IBigFileDownloader mBigFileDownloader;
readonly IBigFileChecker mBigFileChecker;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 216ed3a0f59e467dabfeace5c550c123
timeCreated: 1772182545
@@ -0,0 +1,221 @@
using System;
using Codice.Client.Common.Threading;
using Codice.CM.Client.Differences;
using Codice.CM.Client.Differences.Graphic;
using MergetoolGui;
using Unity.PlasticSCM.Editor.Diff.Text;
using Unity.PlasticSCM.Editor.Diff.Purged;
using Unity.PlasticSCM.Editor.Diff.Texture.Toolbar;
using UnityEngine.Rendering;
using UnityEngine.UIElements;
using XDiffGui;
namespace Unity.PlasticSCM.Editor.Diff.Texture
{
internal class ImageDiffControl : VisualElement, IBigFilePanelListener
{
internal ImageDiffControl(
IBigFileDownloader bigFileDownloader,
IBigFileChecker bigFileChecker)
{
mBigFileDownloader = bigFileDownloader;
mBigFileChecker = bigFileChecker;
CreateGUI();
}
internal void ShowData(DiffViewerData data)
{
mDiffViewerData = data;
ReplaceMainView(mNormalContentPanel);
if (BigFileDiffCalculator.IsBigFileDiff(data, mBigFileDownloader, mBigFileChecker))
{
ShowBigFileMessage();
return;
}
if (data.Left?.IsPurged == true || data.Right?.IsPurged == true)
{
ShowPurgedRevision(data);
return;
}
ShowDiff();
}
internal void Dispose()
{
if (mBigFileMessagePanel != null)
mBigFileMessagePanel.Dispose();
if (mPurgedRevisionControl != null)
mPurgedRevisionControl.Dispose();
mImageDiffToolbar.Dispose();
mImageDiffViewer.Dispose();
}
void IBigFilePanelListener.OnCalculateDifferencesButtonClick()
{
mBigFileMessagePanel.Disable();
IThreadWaiter waiter = ThreadWaiter.GetWaiter(10);
waiter.Execute(
/*threadOperationDelegate*/ delegate
{
mBigFileDownloader.DownloadFiles(mDiffViewerData, null);
},
/*afterOperationDelegate*/ delegate
{
mBigFileMessagePanel.Enable();
if (waiter.Exception != null)
{
MergetoolExceptionsHandler.DisplayException(
waiter.Exception);
return;
}
if (mDiffViewerData.Left?.IsPurged == true ||
mDiffViewerData.Right?.IsPurged == true)
{
ShowPurgedRevision(mDiffViewerData);
return;
}
ShowDiff();
});
}
void ShowDiff()
{
try
{
mImageDiffViewer.SetInfo(
mDiffViewerData.Left.File,
mDiffViewerData.Right.File,
mDiffViewerData.Left.SymbolicName,
mDiffViewerData.Right.SymbolicName);
mMessagePanel.HandleMessage(mDiffViewerData.Message);
EnableToolBar();
ReplaceView(mImageDiffViewer);
}
catch (Exception ex)
{
mMessagePanel.HandleMessage(ex.Message);
DisableToolbar();
}
}
void ShowBigFileMessage()
{
if (mBigFileMessagePanel == null)
mBigFileMessagePanel = new BigFileMessagePanel(this, true);
mBigFileMessagePanel.UpdateDisplayData(
BigFileDisplayData.Build(
mDiffViewerData, mBigFileDownloader));
DisableToolbar();
mMessagePanel.HandleMessage(string.Empty);
ReplaceView(mBigFileMessagePanel);
}
void DisableToolbar()
{
mImageDiffToolbar.SetEnabled(false);
}
void EnableToolBar()
{
mImageDiffToolbar.SetEnabled(true);
}
void ShowPurgedRevision(DiffViewerData data)
{
if (mPurgedRevisionControl == null)
mPurgedRevisionControl = new PurgedRevisionControl();
mPurgedRevisionControl.ShowData(data);
ReplaceMainView(mPurgedRevisionControl);
}
void ReplaceMainView(VisualElement panel)
{
if (hierarchy.childCount == 1 && hierarchy[0] == panel)
return;
Clear();
Add(panel);
}
void ReplaceView(VisualElement view)
{
if (mCurrentView == view)
return;
if (mCurrentView != null)
mContainerPanel.Remove(mCurrentView);
mContainerPanel.Add(view);
mCurrentView = view;
}
void CreateGUI()
{
mMessagePanel = new MessagePanel();
mImageDiffToolbar = new ImageDiffToolbar();
mImageDiffToolbar.ViewModeChanged += OnViewModeChanged;
mImageDiffToolbar.ChannelModeChanged += OnChannelModeChanged;
mImageDiffViewer = new ImageDiffViewer(
mImageDiffToolbar.ModeSwitcher);
mContainerPanel = new VisualElement();
mContainerPanel.style.flexGrow = 1;
style.flexGrow = 1;
mNormalContentPanel = new VisualElement();
mNormalContentPanel.style.flexGrow = 1;
mNormalContentPanel.Add(mMessagePanel);
mNormalContentPanel.Add(mImageDiffToolbar);
mNormalContentPanel.Add(mContainerPanel);
Add(mNormalContentPanel);
ReplaceView(mImageDiffViewer);
}
void OnViewModeChanged(ImageDiffMode mode)
{
mImageDiffViewer.SetViewMode(mode);
}
void OnChannelModeChanged(ColorWriteMask mode)
{
mImageDiffViewer.SetChannelMode(mode);
}
VisualElement mNormalContentPanel;
VisualElement mContainerPanel;
VisualElement mCurrentView;
MessagePanel mMessagePanel;
ImageDiffToolbar mImageDiffToolbar;
ImageDiffViewer mImageDiffViewer;
PurgedRevisionControl mPurgedRevisionControl;
BigFileMessagePanel mBigFileMessagePanel;
DiffViewerData mDiffViewerData;
readonly IBigFileDownloader mBigFileDownloader;
readonly IBigFileChecker mBigFileChecker;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 60719a83a6d44bd88a47d43f8393574f
timeCreated: 1772182364
@@ -0,0 +1,11 @@
namespace Unity.PlasticSCM.Editor.Diff.Texture
{
internal enum ImageDiffMode
{
None,
OnionSkin,
SideBySide,
Differences,
Swipe
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d534c175e106046ea9874a961d0cf1e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,266 @@
using System;
using ImagesGui.ImageDiff;
using Unity.PlasticSCM.Editor.Diff.Texture.Views;
using Unity.PlasticSCM.Editor.Diff.Texture.Views.Differences;
using Unity.PlasticSCM.Editor.Diff.Texture.Views.OnionSkin;
using Unity.PlasticSCM.Editor.Diff.Texture.Views.SideBySide;
using Unity.PlasticSCM.Editor.Diff.Texture.Views.Swipe;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.Diff.Texture
{
internal class ImageDiffViewer : VisualElement
{
internal ImageDiffViewer(IImageDiffToolbar toolbar)
{
mToolbar = toolbar;
style.flexGrow = 1;
}
internal void Dispose()
{
DestroyTextures();
DestroyDiffTexture();
if (mOnionSkinView != null)
mOnionSkinView.Dispose();
if (mSideBySideView != null)
mSideBySideView.Dispose();
if (mDifferencesView != null)
mDifferencesView.Dispose();
if (mSwipeView != null)
mSwipeView.Dispose();
}
internal void SetInfo(
string leftFile, string rightFile,
string leftSymbolicName, string rightSymbolicName)
{
DestroyTextures();
DestroyDiffTexture();
mLeftTexture = TextureLoader.LoadFromFile(leftFile);
mRightTexture = TextureLoader.LoadFromFile(rightFile);
mToolbar.EnableDifferencesMode();
UpdateCurrentView();
}
internal void SetViewMode(ImageDiffMode mode)
{
if (mCurrentMode == mode)
return;
mCurrentMode = mode;
UpdateCurrentView();
}
internal void SetChannelMode(ColorWriteMask mode)
{
mChannelMode = mode;
if (mDifferencesView != null)
mDifferencesView.SetChannelMode(mode);
if (mSideBySideView != null)
mSideBySideView.SetChannelMode(mode);
if (mOnionSkinView != null)
mOnionSkinView.SetChannelMode(mode);
if (mSwipeView != null)
mSwipeView.SetChannelMode(mode);
}
internal ImageDiffMode CurrentViewMode { get { return mCurrentMode; } }
internal ColorWriteMask CurrentChannelMode { get { return mChannelMode; } }
void UpdateCurrentView()
{
if (mLeftTexture == null && mRightTexture == null)
return;
VisualElement view = GetOrCreateView(mCurrentMode);
ReplaceView(view);
}
VisualElement GetOrCreateView(ImageDiffMode mode)
{
switch (mode)
{
case ImageDiffMode.OnionSkin:
return GetOrCreateOnionSkinView();
case ImageDiffMode.Differences:
return GetOrCreateDifferencesView();
case ImageDiffMode.Swipe:
return GetOrCreateSwipeView();
case ImageDiffMode.SideBySide:
default:
return GetOrCreateSideBySideView();
}
}
VisualElement GetOrCreateOnionSkinView()
{
if (mOnionSkinView == null)
mOnionSkinView = new OnionSkinView();
mOnionSkinView.SetImages(mLeftTexture, mRightTexture);
mOnionSkinView.SetChannelMode(mChannelMode);
return mOnionSkinView;
}
VisualElement GetOrCreateSideBySideView()
{
if (mSideBySideView == null)
mSideBySideView = new SideBySideView();
mSideBySideView.SetImages(mLeftTexture, mRightTexture);
mSideBySideView.SetChannelMode(mChannelMode);
return mSideBySideView;
}
VisualElement GetOrCreateDifferencesView()
{
if (mDifferencesView == null)
mDifferencesView = new DifferencesView();
if (mDiffTexture == null)
mDiffTexture = ComputePixelDiff(mLeftTexture, mRightTexture);
mDifferencesView.SetImage(mDiffTexture);
mDifferencesView.EnableZoomButtons();
mDifferencesView.SetChannelMode(mChannelMode);
return mDifferencesView;
}
VisualElement GetOrCreateSwipeView()
{
if (mSwipeView == null)
mSwipeView = new SwipeView();
mSwipeView.SetImages(mLeftTexture, mRightTexture);
mSwipeView.SetChannelMode(mChannelMode);
return mSwipeView;
}
static unsafe Texture2D ComputePixelDiff(
Texture2D leftTexture, Texture2D rightTexture)
{
ImagePixelDiff.GetDiffImageSize(
leftTexture != null ? leftTexture.width : 0,
leftTexture != null ? leftTexture.height : 0,
rightTexture != null ? rightTexture.width : 0,
rightTexture != null ? rightTexture.height : 0,
out int diffWidth, out int diffHeight);
if (diffWidth <= 0 || diffHeight <= 0)
return null;
Color32[] leftPixels = leftTexture != null
? leftTexture.GetPixels32()
: new Color32[0];
Color32[] rightPixels = rightTexture != null
? rightTexture.GetPixels32()
: new Color32[0];
Color32[] diffPixels = new Color32[diffWidth * diffHeight];
int bytesPerPixel = 4;
int leftStride = leftTexture != null
? leftTexture.width * bytesPerPixel : 0;
int rightStride = rightTexture != null
? rightTexture.width * bytesPerPixel : 0;
int diffStride = diffWidth * bytesPerPixel;
fixed (Color32* pLeft = leftPixels)
fixed (Color32* pRight = rightPixels)
fixed (Color32* pDiff = diffPixels)
{
ImagePixelDiff.PixelDiff(
(byte*)pLeft,
leftTexture != null ? leftTexture.width : 0,
leftTexture != null ? leftTexture.height : 0,
leftStride,
(byte*)pRight,
rightTexture != null ? rightTexture.width : 0,
rightTexture != null ? rightTexture.height : 0,
rightStride,
(byte*)pDiff,
diffWidth, diffHeight, diffStride,
bytesPerPixel);
}
Texture2D diffTexture = new Texture2D(
diffWidth, diffHeight, TextureFormat.RGBA32, false);
diffTexture.filterMode = FilterMode.Bilinear;
diffTexture.hideFlags = HideFlags.HideAndDontSave;
diffTexture.SetPixels32(diffPixels);
diffTexture.Apply();
return diffTexture;
}
void ReplaceView(VisualElement view)
{
if (mCurrentView == view)
return;
if (mCurrentView != null)
Remove(mCurrentView);
Add(view);
mCurrentView = view;
}
void DestroyTextures()
{
if (mLeftTexture != null)
{
UnityEngine.Object.DestroyImmediate(mLeftTexture);
mLeftTexture = null;
}
if (mRightTexture != null)
{
UnityEngine.Object.DestroyImmediate(mRightTexture);
mRightTexture = null;
}
}
void DestroyDiffTexture()
{
if (mDiffTexture == null)
return;
UnityEngine.Object.DestroyImmediate(mDiffTexture);
mDiffTexture = null;
}
VisualElement mCurrentView;
ImageDiffMode mCurrentMode = ImageDiffMode.SideBySide;
ColorWriteMask mChannelMode = ColorWriteMask.All;
Texture2D mLeftTexture;
Texture2D mRightTexture;
Texture2D mDiffTexture;
OnionSkinView mOnionSkinView;
SideBySideView mSideBySideView;
DifferencesView mDifferencesView;
SwipeView mSwipeView;
readonly IImageDiffToolbar mToolbar;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d9adb3193c53f469395c117d58322986
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,59 @@
using Unity.PlasticSCM.Editor.Diff.Texture;
using Unity.PlasticSCM.Editor.Diff.Texture.Views.Differences;
using UnityEngine.Rendering;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.Diff.Texture
{
internal class ImageViewPanel : VisualElement
{
internal ImageViewPanel()
{
style.flexGrow = 1;
mDifferencesView = new DifferencesView();
Add(mDifferencesView);
}
internal void ShowImage(string file)
{
DestroyLoadedTexture();
mLoadedTexture = TextureLoader.LoadFromFile(file);
mDifferencesView.SetImage(mLoadedTexture);
mDifferencesView.EnableZoomButtons();
}
internal void ClearImage()
{
mDifferencesView.CleanImage();
mDifferencesView.DisableZoomButtons();
}
internal void SetChannelMode(ColorWriteMask mode)
{
mDifferencesView.SetChannelMode(mode);
}
internal void Dispose()
{
DestroyLoadedTexture();
mDifferencesView.Dispose();
}
void DestroyLoadedTexture()
{
if (mLoadedTexture == null)
return;
UnityEngine.Object.DestroyImmediate(
mLoadedTexture);
mLoadedTexture = null;
}
DifferencesView mDifferencesView;
UnityEngine.Texture mLoadedTexture;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0c4780cc475d491c83bacdc086e8ce0f
timeCreated: 1774439922
@@ -0,0 +1,123 @@
using System;
using System.IO;
using System.Reflection;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
namespace Unity.PlasticSCM.Editor.Diff.Texture
{
internal static class TextureLoader
{
internal static Texture2D LoadFromFile(string filePath)
{
Texture2D texture = TryLoadWithImageConversion(filePath);
if (texture != null)
return texture;
return LoadWithUnityDecoder(filePath);
}
static Texture2D TryLoadWithImageConversion(string filePath)
{
MethodInfo method = GetLoadImageDataAtPathMethod();
if (method == null)
return null;
object[] args = new object[]
{
filePath, 0, 0, 0, GraphicsFormat.None
};
NativeArray<byte> pixelData = (NativeArray<byte>)method.Invoke(null, args);
try
{
int width = (int)args[1];
int height = (int)args[2];
int rowBytes = (int)args[3];
GraphicsFormat format = (GraphicsFormat)args[4];
if (width <= 0 || height <= 0)
return null;
Texture2D texture = new Texture2D(
width, height, format, TextureCreationFlags.None);
texture.filterMode = FilterMode.Bilinear;
texture.hideFlags = HideFlags.HideAndDontSave;
int bytesPerPixel = rowBytes / width;
int tightRowBytes = width * bytesPerPixel;
if (rowBytes == tightRowBytes)
{
texture.SetPixelData(pixelData, 0);
}
else
{
NativeArray<byte> tightData = new NativeArray<byte>(
tightRowBytes * height, Allocator.Temp);
for (int y = 0; y < height; y++)
{
NativeArray<byte>.Copy(
pixelData, y * rowBytes,
tightData, y * tightRowBytes,
tightRowBytes);
}
texture.SetPixelData(tightData, 0);
tightData.Dispose();
}
texture.Apply();
return texture;
}
finally
{
pixelData.Dispose();
}
}
static Texture2D LoadWithUnityDecoder(string filePath)
{
byte[] bytes = File.ReadAllBytes(filePath);
Texture2D texture = new Texture2D(2, 2, TextureFormat.RGBA32, false);
texture.filterMode = FilterMode.Bilinear;
texture.hideFlags = HideFlags.HideAndDontSave;
if (!texture.LoadImage(bytes))
{
UnityEngine.Object.DestroyImmediate(texture);
throw new InvalidOperationException(
"Failed to load image. The format may not be supported.");
}
return texture;
}
static MethodInfo GetLoadImageDataAtPathMethod()
{
if (mbIsMethodCached)
return mCachedMethod;
// we cannot access the internal ImageConversion.LoadImageDataAtPath
// in the UnityEngine.ImageConversionModule assembly,
// so we use reflection to try to get it
mCachedMethod = typeof(ImageConversion).GetMethod(
"LoadImageDataAtPath",
BindingFlags.Static | BindingFlags.NonPublic);
mbIsMethodCached = true;
return mCachedMethod;
}
static MethodInfo mCachedMethod;
static bool mbIsMethodCached;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 349b06c38da6e4ca6b9ad2664e384144
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0b923e169a5f4f878c5dba1c299ee4ab
timeCreated: 1773741066
@@ -0,0 +1,141 @@
using System;
using CodiceApp;
using PlasticGui;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.UI.UIElements;
using UnityEditor.UIElements;
using UnityEngine.Rendering;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.Diff.Texture.Toolbar
{
internal class ChannelOptionsView : VisualElement
{
internal event Action<ColorWriteMask> ChannelModeChanged;
internal ChannelOptionsView()
{
CreateGUI();
}
internal void Dispose()
{
mRgbToggle.UnregisterValueChangedCallback(OnChannelToggleChanged);
mRedToggle.UnregisterValueChangedCallback(OnChannelToggleChanged);
mGreenToggle.UnregisterValueChangedCallback(OnChannelToggleChanged);
mBlueToggle.UnregisterValueChangedCallback(OnChannelToggleChanged);
mAlphaToggle.UnregisterValueChangedCallback(OnChannelToggleChanged);
}
// internal for testing
internal void HandleToggleChanged(
ToolbarToggle changedToggle, bool newValue)
{
if (!newValue)
{
changedToggle.SetValueWithoutNotify(true);
return;
}
SetActiveToggle(changedToggle);
ColorWriteMask mode = GetChannelMode(changedToggle);
ChannelModeChanged?.Invoke(mode);
}
void CreateGUI()
{
style.flexDirection = FlexDirection.Row;
mRgbToggle = CreateChannelToggleLeft(
Images.GetChannelRGBAAllIcon(),
PlasticLocalization.Name.ImageChannelShowRGB.GetString());
mRgbToggle.SetValueWithoutNotify(true);
Add(mRgbToggle);
mRedToggle = CreateChannelToggleLeft(
Images.GetChannelRedIcon(),
PlasticLocalization.Name.ImageChannelShowRed.GetString());
Add(mRedToggle);
mGreenToggle = CreateChannelToggleLeft(
Images.GetChannelGreenIcon(),
PlasticLocalization.Name.ImageChannelShowGreen.GetString());
Add(mGreenToggle);
mBlueToggle = CreateChannelToggleLeft(
Images.GetChannelBlueIcon(),
PlasticLocalization.Name.ImageChannelShowBlue.GetString());
Add(mBlueToggle);
mAlphaToggle = CreateChannelToggle(
Images.GetChannelAlphaIcon(),
PlasticLocalization.Name.ImageChannelShowAlpha.GetString());
Add(mAlphaToggle);
}
ToolbarToggle CreateChannelToggleLeft(
UnityEngine.Texture2D icon, string tooltip)
{
ToolbarToggle toggle = ControlBuilder.Toolbar.CreateImageToggleLeft(
icon, tooltip);
toggle.style.paddingTop = 0;
toggle.RegisterValueChangedCallback(OnChannelToggleChanged);
return toggle;
}
ToolbarToggle CreateChannelToggle(
UnityEngine.Texture2D icon, string tooltip)
{
ToolbarToggle toggle = ControlBuilder.Toolbar.CreateImageToggle(
icon, tooltip);
toggle.style.paddingTop = 0;
toggle.RegisterValueChangedCallback(OnChannelToggleChanged);
return toggle;
}
void OnChannelToggleChanged(ChangeEvent<bool> evt)
{
ToolbarToggle changedToggle = (ToolbarToggle)evt.target;
HandleToggleChanged(changedToggle, evt.newValue);
}
void SetActiveToggle(ToolbarToggle activeToggle)
{
SetToggleValueIfNeeded(mRgbToggle, mRgbToggle == activeToggle);
SetToggleValueIfNeeded(mRedToggle, mRedToggle == activeToggle);
SetToggleValueIfNeeded(mGreenToggle, mGreenToggle == activeToggle);
SetToggleValueIfNeeded(mBlueToggle, mBlueToggle == activeToggle);
SetToggleValueIfNeeded(mAlphaToggle, mAlphaToggle == activeToggle);
}
static void SetToggleValueIfNeeded(ToolbarToggle toggle, bool value)
{
if (toggle.value != value)
toggle.SetValueWithoutNotify(value);
}
ColorWriteMask GetChannelMode(ToolbarToggle toggle)
{
if (toggle == mRedToggle)
return ColorWriteMask.Red;
if (toggle == mGreenToggle)
return ColorWriteMask.Green;
if (toggle == mBlueToggle)
return ColorWriteMask.Blue;
if (toggle == mAlphaToggle)
return ColorWriteMask.Alpha;
return ColorWriteMask.All;
}
ToolbarToggle mRgbToggle;
ToolbarToggle mRedToggle;
ToolbarToggle mGreenToggle;
ToolbarToggle mBlueToggle;
ToolbarToggle mAlphaToggle;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4b4f616cc46fe427abf2c218828fef79
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,67 @@
using System;
using Unity.PlasticSCM.Editor.Diff.Texture;
using Unity.PlasticSCM.Editor.UI.UIElements;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.Diff.Texture.Toolbar
{
internal class ImageDiffToolbar : VisualElement
{
internal event Action<ImageDiffMode> ViewModeChanged;
internal event Action<ColorWriteMask> ChannelModeChanged;
internal IImageDiffToolbar ModeSwitcher
{
get { return mModeSwitcherView; }
}
internal ImageDiffToolbar()
{
CreateGUI();
}
internal void Dispose()
{
mModeSwitcherView.ViewModeChanged -= OnViewModeChanged;
mModeSwitcherView.Dispose();
mChannelOptionsView.ChannelModeChanged -= OnChannelModeChanged;
mChannelOptionsView.Dispose();
}
void CreateGUI()
{
UnityEditor.UIElements.Toolbar toolbar =
ControlBuilder.Toolbar.Create();
mModeSwitcherView = new ImageModeSwitcherView();
mModeSwitcherView.ViewModeChanged += OnViewModeChanged;
toolbar.Add(mModeSwitcherView);
VisualElement spacer = new VisualElement();
spacer.style.flexGrow = 1;
toolbar.Add(spacer);
mChannelOptionsView = new ChannelOptionsView();
mChannelOptionsView.ChannelModeChanged += OnChannelModeChanged;
toolbar.Add(mChannelOptionsView);
Add(toolbar);
}
void OnViewModeChanged(ImageDiffMode mode)
{
ViewModeChanged?.Invoke(mode);
}
void OnChannelModeChanged(ColorWriteMask mode)
{
ChannelModeChanged?.Invoke(mode);
}
ImageModeSwitcherView mModeSwitcherView;
ChannelOptionsView mChannelOptionsView;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ecd2902f8da144961ba11c078bb1e20b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,120 @@
using System;
using Unity.PlasticSCM.Editor.Diff.Texture;
using Unity.PlasticSCM.Editor.UI.UIElements;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.Diff.Texture.Toolbar
{
internal class ImageModeSwitcherView : VisualElement, IImageDiffToolbar
{
internal event Action<ImageDiffMode> ViewModeChanged;
internal ImageModeSwitcherView()
{
CreateGUI();
}
internal void Dispose()
{
mOnionSkinToggle.UnregisterValueChangedCallback(OnModeToggleChanged);
mSideBySideToggle.UnregisterValueChangedCallback(OnModeToggleChanged);
mDifferencesToggle.UnregisterValueChangedCallback(OnModeToggleChanged);
mSwipeToggle.UnregisterValueChangedCallback(OnModeToggleChanged);
}
// internal for testing
internal void HandleToggleChanged(
ToolbarToggle changedToggle, bool newValue)
{
if (!newValue)
{
changedToggle.SetValueWithoutNotify(true);
return;
}
SetActiveToggle(changedToggle);
ImageDiffMode mode = GetMode(changedToggle);
ViewModeChanged?.Invoke(mode);
}
void IImageDiffToolbar.EnableDifferencesMode()
{
mDifferencesToggle.SetEnabled(true);
}
void IImageDiffToolbar.DisableDifferencesMode()
{
mDifferencesToggle.SetEnabled(false);
}
void CreateGUI()
{
style.flexDirection = FlexDirection.Row;
mOnionSkinToggle = CreateModeToggle("Onion skin");
Add(mOnionSkinToggle);
mSideBySideToggle = CreateModeToggle("Side by side");
mSideBySideToggle.SetValueWithoutNotify(true);
Add(mSideBySideToggle);
mDifferencesToggle = CreateModeToggle("Differences");
Add(mDifferencesToggle);
mSwipeToggle = CreateModeToggle("Swipe");
Add(mSwipeToggle);
}
ToolbarToggle CreateModeToggle(string label)
{
ToolbarToggle toggle = new ToolbarToggle();
toggle.text = label;
toggle.style.paddingTop = 0;
toggle.RegisterValueChangedCallback(OnModeToggleChanged);
return toggle;
}
void OnModeToggleChanged(ChangeEvent<bool> evt)
{
ToolbarToggle changedToggle = (ToolbarToggle)evt.target;
HandleToggleChanged(changedToggle, evt.newValue);
}
void SetActiveToggle(ToolbarToggle activeToggle)
{
SetToggleValueIfNeeded(mOnionSkinToggle, mOnionSkinToggle == activeToggle);
SetToggleValueIfNeeded(mSideBySideToggle, mSideBySideToggle == activeToggle);
SetToggleValueIfNeeded(mDifferencesToggle, mDifferencesToggle == activeToggle);
SetToggleValueIfNeeded(mSwipeToggle, mSwipeToggle == activeToggle);
}
static void SetToggleValueIfNeeded(ToolbarToggle toggle, bool value)
{
if (toggle.value != value)
toggle.SetValueWithoutNotify(value);
}
ImageDiffMode GetMode(ToolbarToggle toggle)
{
if (toggle == mOnionSkinToggle)
return ImageDiffMode.OnionSkin;
if (toggle == mDifferencesToggle)
return ImageDiffMode.Differences;
if (toggle == mSwipeToggle)
return ImageDiffMode.Swipe;
return ImageDiffMode.SideBySide;
}
ToolbarToggle mOnionSkinToggle;
ToolbarToggle mSideBySideToggle;
ToolbarToggle mDifferencesToggle;
ToolbarToggle mSwipeToggle;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6e808c3ddcc3246b78a6748007b9aec8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,100 @@
using Unity.PlasticSCM.Editor.Diff.Texture.Views;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.UI.UIElements;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.Diff.Texture.Toolbar
{
internal class ZoomOptionsView : VisualElement
{
internal ZoomOptionsView(IImageDiffView imageDiffView)
{
mImageDiffView = imageDiffView;
CreateGUI();
}
internal void EnableButtons()
{
SetEnabled(true);
}
internal void DisableButtons()
{
SetEnabled(false);
}
internal void Dispose()
{
mZoomInButton.clicked -= OnZoomInClicked;
mZoomOutButton.clicked -= OnZoomOutClicked;
mZoomToFitButton.clicked -= OnZoomToFitClicked;
mZoomOriginalSizeButton.clicked -= OnZoomOriginalSizeClicked;
}
void CreateGUI()
{
style.position = Position.Absolute;
style.right = 20;
style.bottom = 20;
mZoomToFitButton = ControlBuilder.Button.CreateImageButton(
Images.GetZoomToFitIcon(), "Zoom to Fit", OnZoomToFitClicked);
mZoomToFitButton.style.marginRight = 0;
mZoomToFitButton.style.width = BUTTON_SIZE;
mZoomToFitButton.style.height = BUTTON_SIZE;
Add(mZoomToFitButton);
mZoomOriginalSizeButton = ControlBuilder.Button.CreateImageButton(
Images.GetOriginalSizeIcon(), "Original Size", OnZoomOriginalSizeClicked);
mZoomOriginalSizeButton.style.marginRight = 0;
mZoomOriginalSizeButton.style.width = BUTTON_SIZE;
mZoomOriginalSizeButton.style.height = BUTTON_SIZE;
Add(mZoomOriginalSizeButton);
mZoomInButton = ControlBuilder.ButtonGroup.CreateImageTopButton(
Images.GetZoomInIcon(), "Zoom In", OnZoomInClicked);
mZoomInButton.style.marginRight = 0;
mZoomInButton.style.width = BUTTON_SIZE;
mZoomInButton.style.height = BUTTON_SIZE;
Add(mZoomInButton);
mZoomOutButton = ControlBuilder.ButtonGroup.CreateImageBottomButton(
Images.GetZoomOutIcon(), "Zoom Out", OnZoomOutClicked);
mZoomOutButton.style.width = BUTTON_SIZE;
mZoomOutButton.style.height = BUTTON_SIZE;
mZoomOutButton.style.marginRight = 0;
mZoomOutButton.style.marginBottom = 0;
Add(mZoomOutButton);
}
void OnZoomInClicked()
{
mImageDiffView.ZoomIn();
}
void OnZoomOutClicked()
{
mImageDiffView.ZoomOut();
}
void OnZoomToFitClicked()
{
mImageDiffView.ZoomToFit();
}
void OnZoomOriginalSizeClicked()
{
mImageDiffView.ZoomOneToOne();
}
Button mZoomInButton;
Button mZoomOutButton;
Button mZoomOriginalSizeButton;
Button mZoomToFitButton;
readonly IImageDiffView mImageDiffView;
const float BUTTON_SIZE = 30;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: be3e4f1fe8db42108891c86e7f3c66fb
timeCreated: 1773741080
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5c1340178d3942abb42c8b680d301bf1
timeCreated: 1773739167
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6702045c2697453fae61a67931fbef13
timeCreated: 1773739181
@@ -0,0 +1,121 @@
using Unity.PlasticSCM.Editor.Diff.Texture.Toolbar;
using Unity.PlasticSCM.Editor.Diff.Texture.Views.ImageViewer;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.Diff.Texture.Views.Differences
{
internal class DifferencesView : VisualElement, IImageDiffView
{
internal DifferencesView()
{
BuildComponents();
}
internal void SetImage(UnityEngine.Texture preview)
{
mImageView.SetImage(preview);
if (resolvedStyle.width > 0 && resolvedStyle.height > 0)
{
InitZoom(preview);
return;
}
InitZoomWhenLayoutReady(preview);
}
internal void CleanImage()
{
mImageView.CleanImage();
}
internal void SetChannelMode(ColorWriteMask mode)
{
mImageView.SetChannelMode(mode);
}
internal void EnableZoomButtons()
{
mZoomOptionsView.EnableButtons();
}
internal void DisableZoomButtons()
{
mZoomOptionsView.DisableButtons();
}
internal void Dispose()
{
mImageContentView.Dispose();
mZoomOptionsView.Dispose();
}
void IImageDiffView.ZoomIn()
{
mImageContentView.ZoomIn();
}
void IImageDiffView.ZoomOut()
{
mImageContentView.ZoomOut();
}
void IImageDiffView.ZoomOneToOne()
{
mImageContentView.ZoomOneToOne();
}
void IImageDiffView.ZoomToFit()
{
mImageContentView.ZoomToFit();
}
void InitZoomWhenLayoutReady(UnityEngine.Texture image)
{
void OnGeometryChanged(GeometryChangedEvent evt)
{
UnregisterCallback<GeometryChangedEvent>(OnGeometryChanged);
InitZoom(image);
}
RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
}
void InitZoom(UnityEngine.Texture image)
{
if (image == null)
return;
Vector2 frameSize = new Vector2(
resolvedStyle.width, resolvedStyle.height);
Vector2 imageSize = new Vector2(image.width, image.height);
if (ImageDiffExtensions.IsImageBiggerThanFrame(frameSize, imageSize))
{
mImageContentView.InitZoom(mImageContentView.GetZoomValueToFit());
return;
}
mImageContentView.InitZoom(1f);
}
void BuildComponents()
{
style.flexGrow = 1;
mImageView = new ImageView();
mImageContentView = new ImageContentView(mImageView);
mZoomOptionsView = new ZoomOptionsView(this);
Add(mImageContentView);
Add(mZoomOptionsView);
}
ImageView mImageView;
ImageContentView mImageContentView;
ZoomOptionsView mZoomOptionsView;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: be970f1dc8564946b156810a915d9f80
timeCreated: 1773739198
@@ -0,0 +1,10 @@
namespace Unity.PlasticSCM.Editor.Diff.Texture.Views
{
internal interface IImageDiffView
{
void ZoomIn();
void ZoomOut();
void ZoomOneToOne();
void ZoomToFit();
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: edfe946ea0f84261836c83966181e9ec
timeCreated: 1773739257
@@ -0,0 +1,10 @@
using UnityEngine;
namespace Unity.PlasticSCM.Editor.Diff.Texture.Views
{
internal interface IZoomableImageView
{
Vector2 ImageSize { get; }
void SetSize(float width, float height);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 56bfa9b016d534be29da249ecd15b98f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,23 @@
using UnityEngine;
namespace Unity.PlasticSCM.Editor.Diff.Texture.Views
{
internal static class ImageDiffExtensions
{
internal static float CalculateZoomToFit(
float width, float height,
float targetWidth, float targetHeight)
{
if (width <= 0 || height <= 0)
return 1f;
return Mathf.Min(targetWidth / width, targetHeight / height);
}
internal static bool IsImageBiggerThanFrame(
Vector2 frameSize, Vector2 imageSize)
{
return imageSize.x > frameSize.x || imageSize.y > frameSize.y;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 27ad74436f1a14061aeab82ecf55b0a1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 232fff0341b3476d9eae6e83059c7aa0
timeCreated: 1773740952
@@ -0,0 +1,327 @@
using System;
using System.Diagnostics;
using UnityEngine;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.Diff.Texture.Views.ImageViewer
{
internal class ImageContentView : VisualElement
{
internal const float ZOOM_ANIMATION_DURATION_MS = 300f;
internal const long ANIMATION_FRAME_INTERVAL_MS = 16;
internal ScrollView ScrollView { get { return mScrollView; } }
internal float ZoomLevel { get { return mZoomLevel; } }
internal bool DisableAnimationsForTesting { get; set; }
internal ImageContentView(IZoomableImageView imageView)
{
mImageView = imageView;
BuildComponents();
}
internal void Dispose()
{
mScrollView.UnregisterCallback<GeometryChangedEvent>(OnContentViewportGeometryChanged);
mScrollView.UnregisterCallback<WheelEvent>(OnWheelEvent, TrickleDown.TrickleDown);
mScrollView.UnregisterCallback<PointerDownEvent>(OnPointerDown);
mScrollView.UnregisterCallback<PointerUpEvent>(OnPointerUp);
mScrollView.UnregisterCallback<PointerMoveEvent>(OnPointerMove);
mScrollView.UnregisterCallback<PointerLeaveEvent>(OnPointerLeave);
}
internal void HideScrollbars()
{
mHideScrollbars = true;
mScrollView.horizontalScrollerVisibility = ScrollerVisibility.Hidden;
mScrollView.verticalScrollerVisibility = ScrollerVisibility.Hidden;
}
internal void ApplyZoom(float newZoom)
{
CancelZoomAnimation();
mZoomLevel = newZoom;
SetupImageSize(newZoom);
mIsInitialized = true;
}
internal void ApplyZoomWithScrollAnchor(
float newZoom, Vector2 mouseInViewport)
{
CancelZoomAnimation();
float oldZoom = mZoomLevel;
Vector2 mouseInContent = mScrollView.scrollOffset + mouseInViewport;
float ratio = newZoom / oldZoom;
mZoomLevel = newZoom;
SetupImageSize(newZoom);
mIsInitialized = true;
Vector2 newScrollOffset = mouseInContent * ratio - mouseInViewport;
mScrollView.scrollOffset = newScrollOffset;
}
internal void InitZoom(float zoomLevel)
{
CancelZoomAnimation();
mZoomLevel = zoomLevel;
SetupImageSize(zoomLevel);
mIsInitialized = true;
}
internal void ZoomIn()
{
AnimateZoomTo(mZoomLevel + ZOOM_STEP);
}
internal void ZoomOut()
{
AnimateZoomTo(Math.Max(mZoomLevel - ZOOM_STEP, MIN_ZOOM_LEVEL));
}
internal void ZoomOneToOne()
{
AnimateZoomTo(1f);
}
internal void ZoomToFit()
{
AnimateZoomTo(Math.Max(GetZoomValueToFit(), MIN_ZOOM_LEVEL));
}
internal float GetZoomValueToFit()
{
float margin = 4f;
float availableWidth = resolvedStyle.width - margin;
float availableHeight = resolvedStyle.height - margin;
if (float.IsNaN(availableWidth) || float.IsNaN(availableHeight)
|| availableWidth <= 0 || availableHeight <= 0)
return 1f;
return ImageDiffExtensions.CalculateZoomToFit(
mImageView.ImageSize.x, mImageView.ImageSize.y,
availableWidth, availableHeight);
}
void AnimateZoomTo(float targetZoom)
{
if (!mIsInitialized || DisableAnimationsForTesting)
{
mZoomLevel = targetZoom;
SetupImageSize(targetZoom);
return;
}
CancelZoomAnimation();
float startZoom = mZoomLevel;
if (Mathf.Approximately(startZoom, targetZoom))
return;
Stopwatch stopwatch = Stopwatch.StartNew();
mZoomAnimation = schedule.Execute(() =>
{
float elapsed = (float)stopwatch.Elapsed.TotalMilliseconds;
float t = Mathf.Clamp01(elapsed / ZOOM_ANIMATION_DURATION_MS);
t = CubicEaseOut(t);
mZoomLevel = Mathf.Lerp(startZoom, targetZoom, t);
SetupImageSize(mZoomLevel);
if (t >= 1f)
{
mZoomAnimation.Pause();
mZoomAnimation = null;
}
}).Every(ANIMATION_FRAME_INTERVAL_MS);
}
void CancelZoomAnimation()
{
if (mZoomAnimation == null)
return;
mZoomAnimation.Pause();
mZoomAnimation = null;
}
static float CubicEaseOut(float t)
{
t -= 1f;
return t * t * t + 1f;
}
void SetupImageSize(float zoomLevel)
{
Vector2 imageSize = mImageView.ImageSize;
float targetWidth = imageSize.x * zoomLevel;
float targetHeight = imageSize.y * zoomLevel;
mImageView.SetSize(targetWidth, targetHeight);
UpdateScrollerVisibility(targetWidth, targetHeight);
}
void UpdateScrollerVisibility(float imageWidth, float imageHeight)
{
if (mHideScrollbars)
return;
float viewportWidth = mScrollView.layout.width;
float viewportHeight = mScrollView.layout.height;
if (float.IsNaN(viewportWidth) || float.IsNaN(viewportHeight)
|| viewportWidth <= 0 || viewportHeight <= 0)
return;
mScrollView.horizontalScrollerVisibility =
imageWidth > viewportWidth
? ScrollerVisibility.Auto
: ScrollerVisibility.Hidden;
mScrollView.verticalScrollerVisibility =
imageHeight > viewportHeight
? ScrollerVisibility.Auto
: ScrollerVisibility.Hidden;
}
void BuildComponents()
{
style.flexGrow = 1;
style.overflow = Overflow.Hidden;
mScrollView = new ScrollView(ScrollViewMode.VerticalAndHorizontal);
mScrollView.style.flexGrow = 1;
mScrollView.horizontalScrollerVisibility = ScrollerVisibility.Auto;
mScrollView.verticalScrollerVisibility = ScrollerVisibility.Auto;
mScrollView.contentContainer.style.alignItems = Align.Center;
mScrollView.contentContainer.style.justifyContent = Justify.Center;
mScrollView.contentContainer.style.flexGrow = 1;
mScrollView.RegisterCallback<GeometryChangedEvent>(OnContentViewportGeometryChanged);
schedule.Execute(UpdateContentContainerMinSize);
mScrollView.Add((VisualElement)mImageView);
mScrollView.RegisterCallback<WheelEvent>(OnWheelEvent, TrickleDown.TrickleDown);
mScrollView.RegisterCallback<PointerDownEvent>(OnPointerDown);
mScrollView.RegisterCallback<PointerUpEvent>(OnPointerUp);
mScrollView.RegisterCallback<PointerMoveEvent>(OnPointerMove);
mScrollView.RegisterCallback<PointerLeaveEvent>(OnPointerLeave);
Add(mScrollView);
}
void OnContentViewportGeometryChanged(GeometryChangedEvent evt)
{
UpdateContentContainerMinSize();
}
void UpdateContentContainerMinSize()
{
float viewportHeight = mScrollView.layout.height;
float viewportWidth = mScrollView.layout.width;
if (viewportHeight > 0 && viewportWidth > 0)
{
mScrollView.contentContainer.style.minHeight = viewportHeight;
mScrollView.contentContainer.style.minWidth = viewportWidth;
}
}
void OnWheelEvent(WheelEvent evt)
{
if (!mIsInitialized)
return;
if (!evt.ctrlKey)
return;
evt.StopPropagation();
CancelZoomAnimation();
float oldZoom = mZoomLevel;
float newZoom = evt.delta.y > 0
? oldZoom / WHEEL_ZOOM_FACTOR
: oldZoom * WHEEL_ZOOM_FACTOR;
newZoom = Mathf.Max(newZoom, MIN_ZOOM_LEVEL);
if (Mathf.Approximately(oldZoom, newZoom))
return;
Vector2 mouseInViewport =
mScrollView.contentViewport.WorldToLocal(evt.mousePosition);
Vector2 mouseInContent = mScrollView.scrollOffset + mouseInViewport;
float ratio = newZoom / oldZoom;
mZoomLevel = newZoom;
SetupImageSize(newZoom);
Vector2 newScrollOffset = mouseInContent * ratio - mouseInViewport;
mScrollView.scrollOffset = newScrollOffset;
}
void OnPointerDown(PointerDownEvent evt)
{
mDragOrigin = (Vector2)evt.localPosition;
mScrollOffsetAtDragStart = mScrollView.scrollOffset;
mIsDragging = true;
mScrollView.CapturePointer(evt.pointerId);
}
void OnPointerUp(PointerUpEvent evt)
{
mIsDragging = false;
if (mScrollView.HasPointerCapture(evt.pointerId))
mScrollView.ReleasePointer(evt.pointerId);
}
void OnPointerMove(PointerMoveEvent evt)
{
if (!mIsDragging)
return;
Vector2 delta = mDragOrigin - (Vector2)evt.localPosition;
mScrollView.scrollOffset = new Vector2(
mScrollOffsetAtDragStart.x + delta.x,
mScrollOffsetAtDragStart.y + delta.y);
}
void OnPointerLeave(PointerLeaveEvent evt)
{
mIsDragging = false;
}
Vector2 mDragOrigin;
Vector2 mScrollOffsetAtDragStart;
bool mIsDragging;
bool mIsInitialized;
bool mHideScrollbars;
float mZoomLevel = 1f;
ScrollView mScrollView;
IZoomableImageView mImageView;
IVisualElementScheduledItem mZoomAnimation;
const float MIN_ZOOM_LEVEL = 0.08f;
const float ZOOM_STEP = 0.1f;
const float WHEEL_ZOOM_FACTOR = 1.1f;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 091b97caa0d248bb8b9edfbc73ba34ad
timeCreated: 1773740999
@@ -0,0 +1,99 @@
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.Diff.Texture.Views.ImageViewer
{
internal class ImageView : VisualElement, IZoomableImageView
{
Vector2 IZoomableImageView.ImageSize
{
get { return ImageSize; }
}
void IZoomableImageView.SetSize(float width, float height)
{
SetSize(width, height);
}
internal Vector2 ImageSize
{
get
{
if (mTexture == null)
return Vector2.zero;
return new Vector2(mTexture.width, mTexture.height);
}
}
internal ImageView()
{
style.overflow = Overflow.Hidden;
mImageElement = new IMGUIContainer(DrawImage);
mImageElement.name = "image";
mImageElement.style.position = Position.Absolute;
mImageElement.style.top = 0;
mImageElement.style.left = 0;
mImageElement.style.right = 0;
mImageElement.style.bottom = 0;
Add(mImageElement);
style.borderTopWidth = 1;
style.borderBottomWidth = 1;
style.borderLeftWidth = 1;
style.borderRightWidth = 1;
}
internal void SetBorderColor(Color color)
{
style.borderTopColor = color;
style.borderBottomColor = color;
style.borderLeftColor = color;
style.borderRightColor = color;
}
internal void SetImage(UnityEngine.Texture texture)
{
mTexture = texture;
mImageElement.MarkDirtyRepaint();
}
internal void CleanImage()
{
mTexture = null;
}
internal void SetChannelMode(ColorWriteMask mode)
{
if (mChannelMode == mode)
return;
mChannelMode = mode;
mImageElement.MarkDirtyRepaint();
}
internal void SetSize(float width, float height)
{
style.width = width;
style.height = height;
}
void DrawImage()
{
DrawTexture.WithColorMask(
mImageElement.contentRect,
mTexture,
ScaleMode.ScaleToFit,
mChannelMode);
}
IMGUIContainer mImageElement;
UnityEngine.Texture mTexture;
ColorWriteMask mChannelMode = ColorWriteMask.All;
Texture2D mFilteredTexture;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d9fac566ed434c58b4d3293cc32b06f2
timeCreated: 1773740981
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e1ed640bd70fd427bbd3b9e95533ce87
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,159 @@
using System;
using Unity.PlasticSCM.Editor.UI;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.Diff.Texture.Views.OnionSkin
{
internal class OnionSkinImageView : VisualElement, IZoomableImageView
{
internal OnionSkinImageView()
{
style.overflow = Overflow.Hidden;
mLeftImage = new IMGUIContainer(DrawLeftImage);
mLeftImage.style.position = Position.Absolute;
SetBorderColor(mLeftImage,
UnityStyles.Colors.ImageDiff.LeftImageBorderColor);
Add(mLeftImage);
mRightImage = new IMGUIContainer(DrawRightImage);
mRightImage.style.position = Position.Absolute;
mRightImage.style.opacity = 0.5f;
SetBorderColor(mRightImage,
UnityStyles.Colors.ImageDiff.RightImageBorderColor);
Add(mRightImage);
}
public Vector2 ImageSize
{
get { return mComposedSize; }
}
public void SetSize(float width, float height)
{
style.width = width;
style.height = height;
UpdateImagePositions(width, height);
}
internal void SetImages(Texture2D leftTexture, Texture2D rightTexture)
{
mLeftTexture = leftTexture;
mRightTexture = rightTexture;
UpdateComposedSize(leftTexture, rightTexture);
mLeftImage.MarkDirtyRepaint();
mLeftImage.MarkDirtyRepaint();
}
internal void SetOpacity(float opacity)
{
mRightOpacity = opacity;
mRightImage.MarkDirtyRepaint();
}
internal void SetChannelMode(ColorWriteMask mode)
{
if (mChannelMode == mode)
return;
mChannelMode = mode;
mLeftImage.MarkDirtyRepaint();
mRightImage.MarkDirtyRepaint();
}
internal void Dispose()
{
mLeftTexture = null;
mRightTexture = null;
}
void UpdateComposedSize(Texture2D leftTexture, Texture2D rightTexture)
{
int leftW = leftTexture != null ? leftTexture.width : 0;
int leftH = leftTexture != null ? leftTexture.height : 0;
int rightW = rightTexture != null ? rightTexture.width : 0;
int rightH = rightTexture != null ? rightTexture.height : 0;
mComposedSize = new Vector2(
Math.Max(leftW, rightW),
Math.Max(leftH, rightH));
}
void UpdateImagePositions(float containerWidth, float containerHeight)
{
if (mComposedSize.x <= 0 || mComposedSize.y <= 0)
return;
UpdateImagePosition(
mLeftImage, mLeftTexture, containerWidth, containerHeight);
UpdateImagePosition(
mRightImage, mRightTexture, containerWidth, containerHeight);
}
void UpdateImagePosition(
IMGUIContainer image,
Texture2D texture,
float containerWidth,
float containerHeight)
{
if (texture == null)
return;
float imageWidth = containerWidth * texture.width / mComposedSize.x;
float imageHeight = containerHeight * texture.height / mComposedSize.y;
image.style.left = (containerWidth - imageWidth) / 2f;
image.style.top = (containerHeight - imageHeight) / 2f;
image.style.width = imageWidth;
image.style.height = imageHeight;
}
static void SetBorderColor(VisualElement element, Color color)
{
element.style.borderTopWidth = 1;
element.style.borderBottomWidth = 1;
element.style.borderLeftWidth = 1;
element.style.borderRightWidth = 1;
element.style.borderTopColor = color;
element.style.borderBottomColor = color;
element.style.borderLeftColor = color;
element.style.borderRightColor = color;
}
void DrawLeftImage()
{
DrawTexture.WithColorMask(
mLeftImage.contentRect,
mLeftTexture,
ScaleMode.ScaleToFit,
mChannelMode);
}
void DrawRightImage()
{
DrawTexture.DrawWithColorMaskAndOpacity(
mRightImage.contentRect,
mRightTexture,
ScaleMode.ScaleToFit,
mChannelMode,
mRightOpacity);
}
float mRightOpacity = 0.5f;
IMGUIContainer mLeftImage;
IMGUIContainer mRightImage;
Texture2D mLeftTexture;
Texture2D mRightTexture;
ColorWriteMask mChannelMode = ColorWriteMask.All;
Vector2 mComposedSize;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 62fe16837ace7440c893c6e7bf705e60
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,125 @@
using Unity.PlasticSCM.Editor.Diff.Texture.Toolbar;
using Unity.PlasticSCM.Editor.Diff.Texture.Views.ImageViewer;
using Unity.PlasticSCM.Editor.UI;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.Diff.Texture.Views.OnionSkin
{
internal class OnionSkinView : VisualElement, IImageDiffView
{
internal OnionSkinView()
{
BuildComponents();
}
internal void SetImages(Texture2D leftTexture, Texture2D rightTexture)
{
mOnionSkinImageView.SetImages(leftTexture, rightTexture);
mOpacitySlider.value = 0.5f;
if (resolvedStyle.width > 0 && resolvedStyle.height > 0)
{
InitZoom();
return;
}
InitZoomWhenLayoutReady();
}
internal void SetChannelMode(ColorWriteMask mode)
{
mOnionSkinImageView.SetChannelMode(mode);
}
internal void Dispose()
{
mOnionSkinImageView.Dispose();
mImageContentView.Dispose();
mZoomOptionsView.Dispose();
}
void IImageDiffView.ZoomIn()
{
mImageContentView.ZoomIn();
}
void IImageDiffView.ZoomOut()
{
mImageContentView.ZoomOut();
}
void IImageDiffView.ZoomOneToOne()
{
mImageContentView.ZoomOneToOne();
}
void IImageDiffView.ZoomToFit()
{
mImageContentView.ZoomToFit();
}
void InitZoom()
{
Vector2 frameSize = new Vector2(
resolvedStyle.width, resolvedStyle.height);
if (ImageDiffExtensions.IsImageBiggerThanFrame(
frameSize, mOnionSkinImageView.ImageSize))
{
mImageContentView.InitZoom(mImageContentView.GetZoomValueToFit());
return;
}
mImageContentView.InitZoom(1f);
}
void InitZoomWhenLayoutReady()
{
void OnGeometryChanged(GeometryChangedEvent evt)
{
UnregisterCallback<GeometryChangedEvent>(OnGeometryChanged);
InitZoom();
}
RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
}
void BuildComponents()
{
style.flexGrow = 1;
VisualElement sliderPanel = new VisualElement();
sliderPanel.style.alignItems = Align.Center;
sliderPanel.style.paddingBottom = 4;
sliderPanel.style.borderBottomWidth = 1;
sliderPanel.style.borderBottomColor = UnityStyles.Colors.BarBorder;
mOpacitySlider = new Slider(0f, 1f);
mOpacitySlider.value = 0.5f;
mOpacitySlider.style.minWidth = 300;
mOpacitySlider.RegisterValueChangedCallback(OnOpacityChanged);
sliderPanel.Add(mOpacitySlider);
Add(sliderPanel);
mOnionSkinImageView = new OnionSkinImageView();
mImageContentView = new ImageContentView(mOnionSkinImageView);
mZoomOptionsView = new ZoomOptionsView(this);
Add(mImageContentView);
Add(mZoomOptionsView);
}
void OnOpacityChanged(ChangeEvent<float> evt)
{
mOnionSkinImageView.SetOpacity(evt.newValue);
}
OnionSkinImageView mOnionSkinImageView;
ImageContentView mImageContentView;
ZoomOptionsView mZoomOptionsView;
Slider mOpacitySlider;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 946fbb19d382b4367840c2d2b4d944f8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ca0b25f6cad2c45419e69a71c9f47695
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,628 @@
using System;
using System.Diagnostics;
using Unity.PlasticSCM.Editor.Diff.Texture.Toolbar;
using Unity.PlasticSCM.Editor.Diff.Texture.Views.ImageViewer;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.UI.UIElements;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.Diff.Texture.Views.SideBySide
{
internal class SideBySideView : VisualElement, IImageDiffView
{
internal SideBySideView()
{
BuildComponents();
}
internal void SetImages(
Texture2D leftTexture, Texture2D rightTexture)
{
mLeftImageView.SetImage(leftTexture);
mRightImageView.SetImage(rightTexture);
if (resolvedStyle.width > 0 && resolvedStyle.height > 0)
{
InitZoom(leftTexture, rightTexture);
return;
}
InitZoomWhenLayoutReady(leftTexture, rightTexture);
}
internal void SetChannelMode(ColorWriteMask mode)
{
mLeftImageView.SetChannelMode(mode);
mRightImageView.SetChannelMode(mode);
}
internal void Dispose()
{
CancelExternalScrollersUpdate();
UnregisterScrollSync();
mPanelsContainer.UnregisterCallback<WheelEvent>(
OnPanelsWheelEvent, TrickleDown.TrickleDown);
UnregisterCallback<GeometryChangedEvent>(
OnGeometryChanged);
mLeftContentView.Dispose();
mRightContentView.Dispose();
mZoomOptionsView.Dispose();
}
void IImageDiffView.ZoomIn()
{
mLeftContentView.ZoomIn();
mRightContentView.ZoomIn();
ScheduleExternalScrollersUpdate();
}
void IImageDiffView.ZoomOut()
{
mLeftContentView.ZoomOut();
mRightContentView.ZoomOut();
ScheduleExternalScrollersUpdate();
}
void IImageDiffView.ZoomOneToOne()
{
mLeftContentView.ZoomOneToOne();
mRightContentView.ZoomOneToOne();
ScheduleExternalScrollersUpdate();
}
void IImageDiffView.ZoomToFit()
{
float zoomFit = GetSynchronizedZoomToFit();
mLeftContentView.InitZoom(zoomFit);
mRightContentView.InitZoom(zoomFit);
ScheduleExternalScrollersUpdate();
}
float GetSynchronizedZoomToFit()
{
float leftFit = mLeftContentView.GetZoomValueToFit();
float rightFit = mRightContentView.GetZoomValueToFit();
return Math.Min(leftFit, rightFit);
}
void InitZoomWhenLayoutReady(
Texture2D leftTexture, Texture2D rightTexture)
{
void OnInitZoomGeometryChanged(GeometryChangedEvent evt)
{
UnregisterCallback<GeometryChangedEvent>(
OnInitZoomGeometryChanged);
InitZoom(leftTexture, rightTexture);
}
RegisterCallback<GeometryChangedEvent>(
OnInitZoomGeometryChanged);
}
void InitZoom(Texture2D leftTexture, Texture2D rightTexture)
{
if (leftTexture == null && rightTexture == null)
return;
Vector2 frameSize = new Vector2(
resolvedStyle.width / 2f, resolvedStyle.height);
Vector2 leftSize = leftTexture != null
? new Vector2(leftTexture.width, leftTexture.height)
: Vector2.zero;
Vector2 rightSize = rightTexture != null
? new Vector2(rightTexture.width, rightTexture.height)
: Vector2.zero;
bool bNeedsFit =
ImageDiffExtensions.IsImageBiggerThanFrame(
frameSize, leftSize) ||
ImageDiffExtensions.IsImageBiggerThanFrame(
frameSize, rightSize);
if (bNeedsFit)
{
float zoomFit = GetSynchronizedZoomToFit();
mLeftContentView.InitZoom(zoomFit);
mRightContentView.InitZoom(zoomFit);
}
else
{
mLeftContentView.InitZoom(1f);
mRightContentView.InitZoom(1f);
}
schedule.Execute(UpdateExternalScrollers);
}
void BuildComponents()
{
style.flexGrow = 1;
style.flexDirection = FlexDirection.Column;
BuildMainRow();
BuildBottomRow();
BuildZoomOptions();
mLeftContentView.HideScrollbars();
mRightContentView.HideScrollbars();
RegisterScrollSync();
mPanelsContainer.RegisterCallback<WheelEvent>(
OnPanelsWheelEvent, TrickleDown.TrickleDown);
RegisterCallback<GeometryChangedEvent>(
OnGeometryChanged);
}
void BuildMainRow()
{
VisualElement mainRow = new VisualElement();
mainRow.style.flexDirection = FlexDirection.Row;
mainRow.style.flexGrow = 1;
mPanelsContainer = new VisualElement();
mPanelsContainer.style.flexDirection = FlexDirection.Row;
mPanelsContainer.style.flexGrow = 1;
mPanelsContainer.style.overflow = Overflow.Hidden;
mLeftImageView = new ImageView();
mLeftImageView.SetBorderColor(
UnityStyles.Colors.ImageDiff.LeftImageBorderColor);
mLeftContentView = new ImageContentView(mLeftImageView);
mLeftContentView.style.flexGrow = 1;
mLeftContentView.style.flexBasis = 0;
BuildSplitter();
mRightImageView = new ImageView();
mRightImageView.SetBorderColor(
UnityStyles.Colors.ImageDiff.RightImageBorderColor);
mRightContentView = new ImageContentView(mRightImageView);
mRightContentView.style.flexGrow = 1;
mRightContentView.style.flexBasis = 0;
mPanelsContainer.Add(mLeftContentView);
mPanelsContainer.Add(mSplitter);
mPanelsContainer.Add(mRightContentView);
mVerticalScroller = new Scroller(0, 0, OnExternalVerticalScroll,
SliderDirection.Vertical);
mVerticalScroller.style.width = SCROLLER_SIZE;
mainRow.Add(mPanelsContainer);
mainRow.Add(mVerticalScroller);
Add(mainRow);
}
void BuildBottomRow()
{
VisualElement bottomRow = new VisualElement();
bottomRow.style.flexDirection = FlexDirection.Row;
mHorizontalScroller = new Scroller(0, 0, OnExternalHorizontalScroll,
SliderDirection.Horizontal);
mHorizontalScroller.style.flexGrow = 1;
mHorizontalScroller.style.height = SCROLLER_SIZE;
VisualElement corner = new VisualElement();
corner.style.width = SCROLLER_SIZE;
corner.style.height = SCROLLER_SIZE;
bottomRow.Add(mHorizontalScroller);
bottomRow.Add(corner);
Add(bottomRow);
}
void BuildZoomOptions()
{
mZoomOptionsView = new ZoomOptionsView(this);
Add(mZoomOptionsView);
}
void BuildSplitter()
{
mSplitter = new VisualElement();
mSplitter.style.width = SPLITTER_WIDTH;
mSplitter.style.justifyContent = Justify.Center;
mSplitter.style.alignItems = Align.Center;
mSplitter.SetMouseCursor(MouseCursor.SplitResizeLeftRight);
VisualElement splitterLine = new VisualElement();
splitterLine.style.width = 1;
splitterLine.style.flexGrow = 1;
splitterLine.style.backgroundColor =
UnityStyles.Colors.BarBorder;
splitterLine.pickingMode = PickingMode.Ignore;
mSplitter.Add(splitterLine);
mSplitter.RegisterCallback<PointerDownEvent>(OnSplitterPointerDown);
mSplitter.RegisterCallback<PointerMoveEvent>(OnSplitterPointerMove);
mSplitter.RegisterCallback<PointerUpEvent>(OnSplitterPointerUp);
}
void OnSplitterPointerDown(PointerDownEvent evt)
{
mbIsSplitterDragging = true;
mSplitterDragStartX = evt.position.x;
mSplitterDragStartLeftGrow = mLeftContentView.resolvedStyle.flexGrow;
mSplitterDragStartRightGrow = mRightContentView.resolvedStyle.flexGrow;
mSplitter.CapturePointer(evt.pointerId);
evt.StopPropagation();
}
void OnSplitterPointerMove(PointerMoveEvent evt)
{
if (!mbIsSplitterDragging)
return;
float containerWidth = mPanelsContainer.resolvedStyle.width
- SPLITTER_WIDTH;
if (containerWidth <= 0)
return;
float deltaX = evt.position.x - mSplitterDragStartX;
float totalGrow = mSplitterDragStartLeftGrow
+ mSplitterDragStartRightGrow;
float growDelta = totalGrow * (deltaX / containerWidth);
float newLeftGrow = mSplitterDragStartLeftGrow + growDelta;
float newRightGrow = mSplitterDragStartRightGrow - growDelta;
float minGrow = totalGrow * (MIN_PANEL_WIDTH / containerWidth);
if (newLeftGrow < minGrow || newRightGrow < minGrow)
return;
mLeftContentView.style.flexGrow = newLeftGrow;
mRightContentView.style.flexGrow = newRightGrow;
schedule.Execute(UpdateExternalScrollers);
evt.StopPropagation();
}
void OnSplitterPointerUp(PointerUpEvent evt)
{
mbIsSplitterDragging = false;
if (mSplitter.HasPointerCapture(evt.pointerId))
mSplitter.ReleasePointer(evt.pointerId);
evt.StopPropagation();
}
void RegisterScrollSync()
{
ScrollView leftScroll = mLeftContentView.ScrollView;
ScrollView rightScroll = mRightContentView.ScrollView;
leftScroll.horizontalScroller.valueChanged +=
OnLeftHorizontalScrollChanged;
leftScroll.verticalScroller.valueChanged +=
OnLeftVerticalScrollChanged;
rightScroll.horizontalScroller.valueChanged +=
OnRightHorizontalScrollChanged;
rightScroll.verticalScroller.valueChanged +=
OnRightVerticalScrollChanged;
}
void UnregisterScrollSync()
{
ScrollView leftScroll = mLeftContentView.ScrollView;
ScrollView rightScroll = mRightContentView.ScrollView;
leftScroll.horizontalScroller.valueChanged -=
OnLeftHorizontalScrollChanged;
leftScroll.verticalScroller.valueChanged -=
OnLeftVerticalScrollChanged;
rightScroll.horizontalScroller.valueChanged -=
OnRightHorizontalScrollChanged;
rightScroll.verticalScroller.valueChanged -=
OnRightVerticalScrollChanged;
}
void OnLeftHorizontalScrollChanged(float value)
{
if (mbIsSyncing)
return;
mbIsSyncing = true;
mRightContentView.ScrollView.horizontalScroller.value = value;
UpdateExternalHorizontalScrollerValue();
mbIsSyncing = false;
}
void OnLeftVerticalScrollChanged(float value)
{
if (mbIsSyncing)
return;
mbIsSyncing = true;
mRightContentView.ScrollView.verticalScroller.value = value;
UpdateExternalVerticalScrollerValue();
mbIsSyncing = false;
}
void OnRightHorizontalScrollChanged(float value)
{
if (mbIsSyncing)
return;
mbIsSyncing = true;
mLeftContentView.ScrollView.horizontalScroller.value = value;
UpdateExternalHorizontalScrollerValue();
mbIsSyncing = false;
}
void OnRightVerticalScrollChanged(float value)
{
if (mbIsSyncing)
return;
mbIsSyncing = true;
mLeftContentView.ScrollView.verticalScroller.value = value;
UpdateExternalVerticalScrollerValue();
mbIsSyncing = false;
}
void OnExternalHorizontalScroll(float value)
{
if (mbIsSyncing)
return;
mbIsSyncing = true;
mLeftContentView.ScrollView.scrollOffset = new Vector2(
value, mLeftContentView.ScrollView.scrollOffset.y);
mRightContentView.ScrollView.scrollOffset = new Vector2(
value, mRightContentView.ScrollView.scrollOffset.y);
mbIsSyncing = false;
}
void OnExternalVerticalScroll(float value)
{
if (mbIsSyncing)
return;
mbIsSyncing = true;
mLeftContentView.ScrollView.scrollOffset = new Vector2(
mLeftContentView.ScrollView.scrollOffset.x, value);
mRightContentView.ScrollView.scrollOffset = new Vector2(
mRightContentView.ScrollView.scrollOffset.x, value);
mbIsSyncing = false;
}
void OnPanelsWheelEvent(WheelEvent evt)
{
if (!evt.ctrlKey)
return;
evt.StopPropagation();
float oldZoom = mLeftContentView.ZoomLevel;
float newZoom = evt.delta.y > 0
? oldZoom / WHEEL_ZOOM_FACTOR
: oldZoom * WHEEL_ZOOM_FACTOR;
newZoom = Mathf.Max(newZoom, MIN_ZOOM_LEVEL);
if (Mathf.Approximately(oldZoom, newZoom))
return;
bool bIsMouseOverLeft = IsMouseOverPanel(
evt.mousePosition, mLeftContentView);
ImageContentView anchorPanel = bIsMouseOverLeft
? mLeftContentView
: mRightContentView;
ImageContentView otherPanel = bIsMouseOverLeft
? mRightContentView
: mLeftContentView;
Vector2 mouseInViewport =
anchorPanel.ScrollView.contentViewport.WorldToLocal(
evt.mousePosition);
anchorPanel.ApplyZoomWithScrollAnchor(newZoom, mouseInViewport);
otherPanel.ApplyZoom(newZoom);
otherPanel.ScrollView.scrollOffset =
anchorPanel.ScrollView.scrollOffset;
UpdateExternalScrollers();
}
void OnGeometryChanged(GeometryChangedEvent evt)
{
schedule.Execute(UpdateExternalScrollers);
}
void UpdateExternalScrollers()
{
UpdateExternalHorizontalScroller();
UpdateExternalVerticalScroller();
}
void ScheduleExternalScrollersUpdate()
{
CancelExternalScrollersUpdate();
Stopwatch stopwatch = Stopwatch.StartNew();
mScrollersUpdateAnimation = schedule.Execute(() =>
{
UpdateExternalScrollers();
float elapsed =
(float)stopwatch.Elapsed.TotalMilliseconds;
if (elapsed >= ImageContentView.ZOOM_ANIMATION_DURATION_MS)
{
mScrollersUpdateAnimation.Pause();
mScrollersUpdateAnimation = null;
}
}).Every(ImageContentView.ANIMATION_FRAME_INTERVAL_MS);
}
void CancelExternalScrollersUpdate()
{
if (mScrollersUpdateAnimation == null)
return;
mScrollersUpdateAnimation.Pause();
mScrollersUpdateAnimation = null;
}
void UpdateExternalHorizontalScroller()
{
float leftExtent = GetHorizontalContentExtent(mLeftContentView);
float rightExtent = GetHorizontalContentExtent(mRightContentView);
float leftViewportWidth =
mLeftContentView.ScrollView.contentViewport.resolvedStyle.width;
float rightViewportWidth =
mRightContentView.ScrollView.contentViewport.resolvedStyle.width;
if (float.IsNaN(leftViewportWidth) || leftViewportWidth <= 0
|| float.IsNaN(rightViewportWidth) || rightViewportWidth <= 0)
return;
float minViewportWidth = Math.Min(
leftViewportWidth, rightViewportWidth);
float maxExtent = Math.Max(leftExtent, rightExtent);
bool bOverflows = leftExtent > leftViewportWidth
|| rightExtent > rightViewportWidth;
mHorizontalScroller.SetEnabled(bOverflows);
mHorizontalScroller.lowValue = 0;
mHorizontalScroller.highValue = bOverflows
? maxExtent - minViewportWidth : 0;
mHorizontalScroller.slider.pageSize = minViewportWidth;
mHorizontalScroller.Adjust(
bOverflows ? minViewportWidth / maxExtent : 1f);
if (bOverflows)
UpdateExternalHorizontalScrollerValue();
else
mHorizontalScroller.value = 0;
}
void UpdateExternalVerticalScroller()
{
float leftExtent = GetVerticalContentExtent(mLeftContentView);
float rightExtent = GetVerticalContentExtent(mRightContentView);
float leftViewportHeight =
mLeftContentView.ScrollView.contentViewport.resolvedStyle.height;
float rightViewportHeight =
mRightContentView.ScrollView.contentViewport.resolvedStyle.height;
if (float.IsNaN(leftViewportHeight) || leftViewportHeight <= 0
|| float.IsNaN(rightViewportHeight) || rightViewportHeight <= 0)
return;
float minViewportHeight = Math.Min(
leftViewportHeight, rightViewportHeight);
float maxExtent = Math.Max(leftExtent, rightExtent);
bool bOverflows = leftExtent > leftViewportHeight
|| rightExtent > rightViewportHeight;
mVerticalScroller.SetEnabled(bOverflows);
mVerticalScroller.lowValue = 0;
mVerticalScroller.highValue = bOverflows
? maxExtent - minViewportHeight : 0;
mVerticalScroller.slider.pageSize = minViewportHeight;
mVerticalScroller.Adjust(
bOverflows ? minViewportHeight / maxExtent : 1f);
if (bOverflows)
UpdateExternalVerticalScrollerValue();
else
mVerticalScroller.value = 0;
}
void UpdateExternalHorizontalScrollerValue()
{
float leftOffset =
mLeftContentView.ScrollView.scrollOffset.x;
float rightOffset =
mRightContentView.ScrollView.scrollOffset.x;
mHorizontalScroller.value = Math.Max(leftOffset, rightOffset);
}
void UpdateExternalVerticalScrollerValue()
{
float leftOffset =
mLeftContentView.ScrollView.scrollOffset.y;
float rightOffset =
mRightContentView.ScrollView.scrollOffset.y;
mVerticalScroller.value = Math.Max(leftOffset, rightOffset);
}
static float GetHorizontalContentExtent(ImageContentView contentView)
{
float contentWidth =
contentView.ScrollView.contentContainer.resolvedStyle.width;
return float.IsNaN(contentWidth) ? 0 : contentWidth;
}
static float GetVerticalContentExtent(ImageContentView contentView)
{
float contentHeight =
contentView.ScrollView.contentContainer.resolvedStyle.height;
return float.IsNaN(contentHeight) ? 0 : contentHeight;
}
static bool IsMouseOverPanel(
Vector2 mousePosition, ImageContentView panel)
{
Vector2 localPos = panel.WorldToLocal(mousePosition);
return panel.contentRect.Contains(localPos);
}
ImageView mLeftImageView;
ImageView mRightImageView;
ImageContentView mLeftContentView;
ImageContentView mRightContentView;
ZoomOptionsView mZoomOptionsView;
VisualElement mPanelsContainer;
VisualElement mSplitter;
Scroller mHorizontalScroller;
Scroller mVerticalScroller;
bool mbIsSyncing;
bool mbIsSplitterDragging;
float mSplitterDragStartX;
float mSplitterDragStartLeftGrow;
float mSplitterDragStartRightGrow;
IVisualElementScheduledItem mScrollersUpdateAnimation;
const float SPLITTER_WIDTH = 8f;
const float MIN_PANEL_WIDTH = 50f;
const float MIN_ZOOM_LEVEL = 0.08f;
const float WHEEL_ZOOM_FACTOR = 1.1f;
const float SCROLLER_SIZE = 15f;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 24c9e4a077ecf4e209e757226ef87899
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 215b9f47294ba41a09021a89ed5f5dc1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,437 @@
using System;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.UI.UIElements;
using UnityEngine.Rendering;
namespace Unity.PlasticSCM.Editor.Diff.Texture.Views.Swipe
{
internal class SwipeImageView : VisualElement, IZoomableImageView
{
internal SwipeImageView()
{
CreateGUI();
RegisterCallback<PointerDownEvent>(OnPointerDown);
RegisterCallback<PointerUpEvent>(OnPointerUp);
RegisterCallback<PointerMoveEvent>(OnPointerMove);
RegisterCallback<AttachToPanelEvent>(OnAttachToPanel);
RegisterCallback<DetachFromPanelEvent>(OnDetachFromPanel);
}
internal void SetImages(Texture2D leftTexture, Texture2D rightTexture)
{
mLeftTexture = leftTexture;
mRightTexture = rightTexture;
UpdateComposedSize(leftTexture, rightTexture);
mSwipePosition = 0.5f;
mLeftImage.MarkDirtyRepaint();
mRightImage.MarkDirtyRepaint();
}
internal void SetChannelMode(ColorWriteMask mode)
{
if (mChannelMode == mode)
return;
mChannelMode = mode;
mLeftImage.MarkDirtyRepaint();
mRightImage.MarkDirtyRepaint();
}
internal void Dispose()
{
UnregisterCallback<PointerDownEvent>(OnPointerDown);
UnregisterCallback<PointerUpEvent>(OnPointerUp);
UnregisterCallback<PointerMoveEvent>(OnPointerMove);
UnregisterCallback<AttachToPanelEvent>(OnAttachToPanel);
UnregisterCallback<DetachFromPanelEvent>(OnDetachFromPanel);
UnregisterScrollCallbacks();
}
Vector2 IZoomableImageView.ImageSize
{
get { return mComposedSize; }
}
void IZoomableImageView.SetSize(float width, float height)
{
style.width = width;
style.height = height;
UpdateLayout(width, height);
}
void OnAttachToPanel(AttachToPanelEvent evt)
{
mScrollView = GetFirstAncestorOfType<ScrollView>();
RegisterScrollCallbacks();
}
void OnDetachFromPanel(DetachFromPanelEvent evt)
{
UnregisterScrollCallbacks();
}
void RegisterScrollCallbacks()
{
if (mScrollView == null)
return;
mScrollView.verticalScroller.valueChanged += OnScrollChanged;
mScrollView.contentViewport.RegisterCallback<GeometryChangedEvent>(
OnViewportGeometryChanged);
}
void UnregisterScrollCallbacks()
{
if (mScrollView == null)
return;
mScrollView.verticalScroller.valueChanged -= OnScrollChanged;
mScrollView.contentViewport.UnregisterCallback<GeometryChangedEvent>(
OnViewportGeometryChanged);
mScrollView = null;
}
void OnScrollChanged(float value)
{
UpdateHandleVerticalPosition();
}
void OnViewportGeometryChanged(GeometryChangedEvent evt)
{
UpdateHandleVerticalPosition();
}
void UpdateHandleVerticalPosition()
{
float containerHeight = resolvedStyle.height;
float containerWidth = resolvedStyle.width;
if (float.IsNaN(containerHeight) || containerHeight <= 0)
return;
if (float.IsNaN(containerWidth) || containerWidth <= 0)
return;
mSwipeHandle.visible =
containerHeight > HANDLE_SIZE * 2 &&
containerWidth > HANDLE_SIZE * 2;
mSwipeHandle.style.top = GetHandleCenterY(containerHeight) - HANDLE_SIZE / 2f;
}
void UpdateComposedSize(Texture2D leftTexture, Texture2D rightTexture)
{
int leftW = leftTexture != null ? leftTexture.width : 0;
int leftH = leftTexture != null ? leftTexture.height : 0;
int rightW = rightTexture != null ? rightTexture.width : 0;
int rightH = rightTexture != null ? rightTexture.height : 0;
mComposedSize = new Vector2(
Math.Max(leftW, rightW),
Math.Max(leftH, rightH));
}
void OnPointerDown(PointerDownEvent evt)
{
mIsDragging = true;
this.CapturePointer(evt.pointerId);
UpdateSwipeFromPointer(evt.localPosition);
evt.StopPropagation();
}
void OnPointerUp(PointerUpEvent evt)
{
mIsDragging = false;
if (this.HasPointerCapture(evt.pointerId))
this.ReleasePointer(evt.pointerId);
}
void OnPointerMove(PointerMoveEvent evt)
{
if (!mIsDragging)
return;
UpdateSwipeFromPointer(evt.localPosition);
}
void UpdateSwipeFromPointer(Vector2 localPosition)
{
float width = resolvedStyle.width;
if (float.IsNaN(width) || width <= 0)
return;
mSwipePosition = Mathf.Clamp01(localPosition.x / width);
UpdateLayout(width, resolvedStyle.height);
}
void UpdateLayout(float containerWidth, float containerHeight)
{
if (containerWidth <= 0 || containerHeight <= 0)
return;
float swipeX = containerWidth * mSwipePosition;
mLeftClipContainer.style.width = swipeX;
mRightClipContainer.style.left = swipeX;
UpdateImagePosition(
mLeftImage, mLeftTexture, 0, containerWidth, containerHeight);
UpdateImagePosition(
mRightImage, mRightTexture, swipeX, containerWidth, containerHeight);
mSwipeLine.style.left = swipeX - SWIPE_LINE_HIT_WIDTH / 2f;
mSwipeHandle.visible =
containerHeight > HANDLE_SIZE * 2 &&
containerWidth > HANDLE_SIZE * 2;
mSwipeHandle.style.left = swipeX - HANDLE_SIZE / 2f;
mSwipeHandle.style.top = GetHandleCenterY(containerHeight) - HANDLE_SIZE / 2f;
}
void UpdateImagePosition(
IMGUIContainer image,
Texture2D texture,
float clipOffset,
float containerWidth,
float containerHeight)
{
if (texture == null || mComposedSize.x <= 0 || mComposedSize.y <= 0)
return;
float imageWidth = containerWidth * texture.width / mComposedSize.x;
float imageHeight = containerHeight * texture.height / mComposedSize.y;
image.style.left = (containerWidth - imageWidth) / 2f - clipOffset;
image.style.top = (containerHeight - imageHeight) / 2f;
image.style.width = imageWidth;
image.style.height = imageHeight;
}
float GetHandleCenterY(float containerHeight)
{
if (mScrollView == null)
return containerHeight / 2f;
VisualElement viewport = mScrollView.contentViewport;
float viewportHeight = viewport.resolvedStyle.height;
if (float.IsNaN(viewportHeight) || viewportHeight <= 0)
return containerHeight / 2f;
if (containerHeight <= viewportHeight)
return containerHeight / 2f;
Vector2 viewportCenter = new Vector2(0, viewportHeight / 2f);
Vector2 localCenter = viewport.ChangeCoordinatesTo(this, viewportCenter);
return Mathf.Clamp(
localCenter.y, HANDLE_SIZE / 2f,
containerHeight - HANDLE_SIZE / 2f);
}
void CreateGUI()
{
style.overflow = Overflow.Visible;
mLeftClipContainer = CreateClipContainer(isLeft: true);
Add(mLeftClipContainer);
mLeftImage = new IMGUIContainer(DrawLeftImage);
mLeftImage.pickingMode = PickingMode.Ignore;
mLeftImage.style.position = Position.Absolute;
SetBorderColor(mLeftImage,
UnityStyles.Colors.ImageDiff.LeftImageBorderColor);
mLeftClipContainer.Add(mLeftImage);
mRightClipContainer = CreateClipContainer(isLeft: false);
Add(mRightClipContainer);
mRightImage = new IMGUIContainer(DrawRightImage);
mRightImage.pickingMode = PickingMode.Ignore;
mRightImage.style.position = Position.Absolute;
SetBorderColor(mRightImage,
UnityStyles.Colors.ImageDiff.RightImageBorderColor);
mRightClipContainer.Add(mRightImage);
mSwipeLine = CreateSwipeLine();
Add(mSwipeLine);
mSwipeHandle = CreateSwipeHandle();
Add(mSwipeHandle);
}
static IMGUIContainer CreateCheckerboard()
{
IMGUIContainer container = new IMGUIContainer();
container.pickingMode = PickingMode.Ignore;
container.style.position = Position.Absolute;
container.style.top = 0;
container.style.left = 0;
container.style.right = 0;
container.style.bottom = 0;
return container;
}
void DrawLeftImage()
{
DrawTexture.WithColorMask(
mLeftImage.contentRect,
mLeftTexture,
ScaleMode.ScaleToFit,
mChannelMode);
}
void DrawRightImage()
{
DrawTexture.WithColorMask(
mRightImage.contentRect,
mRightTexture,
ScaleMode.ScaleToFit,
mChannelMode);
}
static VisualElement CreateClipContainer(bool isLeft)
{
VisualElement container = new VisualElement();
container.pickingMode = PickingMode.Ignore;
container.style.position = Position.Absolute;
container.style.top = 0;
container.style.bottom = 0;
container.style.overflow = Overflow.Hidden;
if (isLeft)
container.style.left = 0;
else
container.style.right = 0;
return container;
}
static VisualElement CreateSwipeLine()
{
VisualElement line = new VisualElement();
line.style.position = Position.Absolute;
line.style.top = 0;
line.style.bottom = 0;
line.style.width = SWIPE_LINE_HIT_WIDTH;
line.SetMouseCursor(MouseCursor.SplitResizeLeftRight);
VisualElement lineVisual = new VisualElement();
lineVisual.pickingMode = PickingMode.Ignore;
lineVisual.style.position = Position.Absolute;
lineVisual.style.top = 0;
lineVisual.style.bottom = 0;
lineVisual.style.width = SWIPE_LINE_VISUAL_WIDTH;
lineVisual.style.left = (SWIPE_LINE_HIT_WIDTH - SWIPE_LINE_VISUAL_WIDTH) / 2f;
lineVisual.style.backgroundColor =
UnityStyles.Colors.ImageDiff.SwipeHandleBorderColor;
line.Add(lineVisual);
return line;
}
static VisualElement CreateSwipeHandle()
{
VisualElement handle = new VisualElement();
handle.style.position = Position.Absolute;
handle.style.width = HANDLE_SIZE;
handle.style.height = HANDLE_SIZE;
handle.style.borderTopLeftRadius = HANDLE_SIZE / 2f;
handle.style.borderTopRightRadius = HANDLE_SIZE / 2f;
handle.style.borderBottomLeftRadius = HANDLE_SIZE / 2f;
handle.style.borderBottomRightRadius = HANDLE_SIZE / 2f;
handle.style.backgroundColor =
UnityStyles.Colors.ImageDiff.SwipeHandleBackgroundColor;
handle.style.borderTopColor =
UnityStyles.Colors.ImageDiff.SwipeHandleBorderColor;
handle.style.borderBottomColor =
UnityStyles.Colors.ImageDiff.SwipeHandleBorderColor;
handle.style.borderLeftColor =
UnityStyles.Colors.ImageDiff.SwipeHandleBorderColor;
handle.style.borderRightColor =
UnityStyles.Colors.ImageDiff.SwipeHandleBorderColor;
handle.style.borderTopWidth = SWIPE_LINE_VISUAL_WIDTH;
handle.style.borderBottomWidth = SWIPE_LINE_VISUAL_WIDTH;
handle.style.borderLeftWidth = SWIPE_LINE_VISUAL_WIDTH;
handle.style.borderRightWidth = SWIPE_LINE_VISUAL_WIDTH;
handle.SetMouseCursor(MouseCursor.SplitResizeLeftRight);
handle.Add(CreateHandleIcon(
Images.GetArrowCaretLeftIcon(), isLeft: true));
handle.Add(CreateHandleIcon(
Images.GetArrowCaretRightIcon(), isLeft: false));
return handle;
}
static Image CreateHandleIcon(Texture2D icon, bool isLeft)
{
Image image = new Image();
image.pickingMode = PickingMode.Ignore;
image.image = icon;
image.scaleMode = ScaleMode.ScaleToFit;
image.style.position = Position.Absolute;
image.style.width = HANDLE_ICON_SIZE;
image.style.height = HANDLE_ICON_SIZE;
image.style.top = HANDLE_ICON_VERTICAL_MARGIN;
image.tintColor = UnityStyles.Colors.ImageDiff.SwipeHandleBorderColor;
if (isLeft)
image.style.left = HANDLE_ICON_HORIZONTAL_MARGIN;
else
image.style.right = HANDLE_ICON_HORIZONTAL_MARGIN;
return image;
}
static void SetBorderColor(VisualElement element, Color color)
{
element.style.borderTopWidth = 1;
element.style.borderBottomWidth = 1;
element.style.borderLeftWidth = 1;
element.style.borderRightWidth = 1;
element.style.borderTopColor = color;
element.style.borderBottomColor = color;
element.style.borderLeftColor = color;
element.style.borderRightColor = color;
}
VisualElement mLeftClipContainer;
VisualElement mRightClipContainer;
IMGUIContainer mLeftImage;
IMGUIContainer mRightImage;
VisualElement mSwipeLine;
VisualElement mSwipeHandle;
Texture2D mLeftTexture;
Texture2D mRightTexture;
Texture2D mLeftFilteredTexture;
Texture2D mRightFilteredTexture;
ColorWriteMask mChannelMode = ColorWriteMask.All;
ScrollView mScrollView;
Vector2 mComposedSize;
float mSwipePosition = 0.5f;
bool mIsDragging;
const float HANDLE_SIZE = 30f;
const float HANDLE_ICON_SIZE = 14f;
const float HANDLE_ICON_HORIZONTAL_MARGIN = 1f;
const float HANDLE_ICON_VERTICAL_MARGIN = 6f;
const float SWIPE_LINE_HIT_WIDTH = 16f;
const float SWIPE_LINE_VISUAL_WIDTH = 2f;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c7bdafe2d54d94655bb31e771614817c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,105 @@
using Unity.PlasticSCM.Editor.Diff.Texture.Toolbar;
using Unity.PlasticSCM.Editor.Diff.Texture.Views.ImageViewer;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.Diff.Texture.Views.Swipe
{
internal class SwipeView : VisualElement, IImageDiffView
{
internal SwipeView()
{
BuildComponents();
}
internal void SetImages(Texture2D leftTexture, Texture2D rightTexture)
{
mSwipeImageView.SetImages(leftTexture, rightTexture);
if (resolvedStyle.width > 0 && resolvedStyle.height > 0)
{
InitZoom();
return;
}
InitZoomWhenLayoutReady();
}
internal void SetChannelMode(ColorWriteMask mode)
{
mSwipeImageView.SetChannelMode(mode);
}
internal void Dispose()
{
mSwipeImageView.Dispose();
mImageContentView.Dispose();
mZoomOptionsView.Dispose();
}
void IImageDiffView.ZoomIn()
{
mImageContentView.ZoomIn();
}
void IImageDiffView.ZoomOut()
{
mImageContentView.ZoomOut();
}
void IImageDiffView.ZoomOneToOne()
{
mImageContentView.ZoomOneToOne();
}
void IImageDiffView.ZoomToFit()
{
mImageContentView.ZoomToFit();
}
void InitZoom()
{
Vector2 frameSize = new Vector2(
resolvedStyle.width, resolvedStyle.height);
if (ImageDiffExtensions.IsImageBiggerThanFrame(
frameSize, ((IZoomableImageView)mSwipeImageView).ImageSize))
{
mImageContentView.InitZoom(mImageContentView.GetZoomValueToFit());
return;
}
mImageContentView.InitZoom(1f);
}
void InitZoomWhenLayoutReady()
{
void OnGeometryChanged(GeometryChangedEvent evt)
{
UnregisterCallback<GeometryChangedEvent>(OnGeometryChanged);
InitZoom();
}
RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
}
void BuildComponents()
{
style.flexGrow = 1;
mSwipeImageView = new SwipeImageView();
mImageContentView = new ImageContentView(mSwipeImageView);
mZoomOptionsView = new ZoomOptionsView(this);
Add(mImageContentView);
Add(mZoomOptionsView);
}
SwipeImageView mSwipeImageView;
ImageContentView mImageContentView;
ZoomOptionsView mZoomOptionsView;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e2cfc902bb82b4790b3d33b4863ef546
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: