first commit
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Unity.PlasticSCM.Editor.UI.UIElements
|
||||
{
|
||||
internal class LoadingSpinner : VisualElement
|
||||
{
|
||||
internal LoadingSpinner()
|
||||
{
|
||||
mStarted = false;
|
||||
|
||||
// add child elements to set up centered spinner rotation
|
||||
mSpinner = new VisualElement();
|
||||
Add(mSpinner);
|
||||
|
||||
mSpinner.style.backgroundImage = Images.GetImage(Images.Name.Loading);
|
||||
mSpinner.style.position = Position.Absolute;
|
||||
mSpinner.style.width = 16;
|
||||
mSpinner.style.height = 16;
|
||||
mSpinner.style.left = -8;
|
||||
mSpinner.style.top = -8;
|
||||
|
||||
style.position = Position.Relative;
|
||||
style.width = 16;
|
||||
style.height = 16;
|
||||
style.left = 8;
|
||||
style.top = 8;
|
||||
}
|
||||
|
||||
internal void Dispose()
|
||||
{
|
||||
if (mStarted)
|
||||
EditorApplication.update -= UpdateProgress;
|
||||
}
|
||||
|
||||
internal void Start()
|
||||
{
|
||||
if (mStarted)
|
||||
return;
|
||||
|
||||
mRotation = 0;
|
||||
mLastRotationTime = EditorApplication.timeSinceStartup;
|
||||
|
||||
EditorApplication.update += UpdateProgress;
|
||||
|
||||
mStarted = true;
|
||||
}
|
||||
|
||||
internal void Stop()
|
||||
{
|
||||
if (!mStarted)
|
||||
return;
|
||||
|
||||
EditorApplication.update -= UpdateProgress;
|
||||
|
||||
mStarted = false;
|
||||
}
|
||||
|
||||
void UpdateProgress()
|
||||
{
|
||||
double currentTime = EditorApplication.timeSinceStartup;
|
||||
double deltaTime = currentTime - mLastRotationTime;
|
||||
|
||||
mSpinner.transform.rotation = Quaternion.Euler(0, 0, mRotation);
|
||||
|
||||
mRotation += (int)(ROTATION_SPEED * deltaTime);
|
||||
mRotation = mRotation % 360;
|
||||
if (mRotation < 0) mRotation += 360;
|
||||
|
||||
mLastRotationTime = currentTime;
|
||||
}
|
||||
|
||||
int mRotation;
|
||||
double mLastRotationTime;
|
||||
bool mStarted;
|
||||
VisualElement mSpinner;
|
||||
|
||||
const int ROTATION_SPEED = 360; // Euler degrees per second
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4664c2d6dd034f04eb60d4190ee2ffb7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,161 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine.UIElements;
|
||||
using PlasticGui;
|
||||
|
||||
namespace Unity.PlasticSCM.Editor.UI.UIElements
|
||||
{
|
||||
class ProgressControlsForDialogs :
|
||||
VisualElement,
|
||||
IProgressControls
|
||||
{
|
||||
internal class Data
|
||||
{
|
||||
internal bool IsWaitingAsyncResult;
|
||||
internal float ProgressPercent;
|
||||
internal string ProgressMessage;
|
||||
|
||||
internal MessageType StatusType;
|
||||
internal string StatusMessage;
|
||||
|
||||
internal void CopyInto(Data other)
|
||||
{
|
||||
other.IsWaitingAsyncResult = IsWaitingAsyncResult;
|
||||
other.ProgressPercent = ProgressPercent;
|
||||
other.ProgressMessage = ProgressMessage;
|
||||
other.StatusType = StatusType;
|
||||
other.StatusMessage = StatusMessage;
|
||||
}
|
||||
}
|
||||
|
||||
internal Data ProgressData { get { return mData; } }
|
||||
|
||||
internal void ForcedUpdateProgress()
|
||||
{
|
||||
if (mData.IsWaitingAsyncResult)
|
||||
{
|
||||
mUndefinedProgress.Show();
|
||||
mPercentageLabel.Show();
|
||||
mLoadingSpinner.Start();
|
||||
EditorApplication.update += UpdatePercent;
|
||||
}
|
||||
else
|
||||
{
|
||||
mUndefinedProgress.Collapse();
|
||||
mPercentageLabel.Collapse();
|
||||
mLoadingSpinner.Stop();
|
||||
EditorApplication.update -= UpdatePercent;
|
||||
}
|
||||
|
||||
mStatusLabel.text = mData.StatusMessage;
|
||||
mProgressLabel.text = mData.ProgressMessage;
|
||||
}
|
||||
|
||||
internal void UpdatePercent()
|
||||
{
|
||||
if (mData.ProgressPercent >= 0)
|
||||
mPercentageLabel.text = string.Format("({0}%)", (int)(mData.ProgressPercent * 100));
|
||||
else
|
||||
mPercentageLabel.text = "";
|
||||
}
|
||||
|
||||
internal ProgressControlsForDialogs(
|
||||
VisualElement[] actionControls)
|
||||
{
|
||||
mActionControls = actionControls;
|
||||
|
||||
InitializeLayoutAndStyles();
|
||||
|
||||
BuildComponents();
|
||||
}
|
||||
|
||||
internal void EnableActionControls(bool enable)
|
||||
{
|
||||
if (mActionControls != null)
|
||||
foreach (var control in mActionControls)
|
||||
if (control != null)
|
||||
control.SetEnabled(enable);
|
||||
}
|
||||
|
||||
void IProgressControls.HideProgress()
|
||||
{
|
||||
EnableActionControls(true);
|
||||
|
||||
mData.IsWaitingAsyncResult = false;
|
||||
mData.ProgressMessage = string.Empty;
|
||||
ForcedUpdateProgress();
|
||||
}
|
||||
|
||||
void IProgressControls.ShowProgress(string message)
|
||||
{
|
||||
EnableActionControls(false);
|
||||
|
||||
CleanStatusMessage(mData);
|
||||
|
||||
mData.IsWaitingAsyncResult = true;
|
||||
mData.ProgressPercent = -1f;
|
||||
mData.ProgressMessage = message;
|
||||
ForcedUpdateProgress();
|
||||
}
|
||||
|
||||
void IProgressControls.ShowError(string message)
|
||||
{
|
||||
mData.StatusMessage = message;
|
||||
mData.StatusType = MessageType.Error;
|
||||
ForcedUpdateProgress();
|
||||
}
|
||||
|
||||
void IProgressControls.ShowNotification(string message)
|
||||
{
|
||||
mData.StatusMessage = message;
|
||||
mData.StatusType = MessageType.Info;
|
||||
ForcedUpdateProgress();
|
||||
}
|
||||
|
||||
void IProgressControls.ShowSuccess(string message)
|
||||
{
|
||||
mData.StatusMessage = message;
|
||||
mData.StatusType = MessageType.Info;
|
||||
ForcedUpdateProgress();
|
||||
}
|
||||
|
||||
void IProgressControls.ShowWarning(string message)
|
||||
{
|
||||
mData.StatusMessage = message;
|
||||
mData.StatusType = MessageType.Warning;
|
||||
ForcedUpdateProgress();
|
||||
}
|
||||
|
||||
void BuildComponents()
|
||||
{
|
||||
mUndefinedProgress = this.Q<VisualElement>("UndefinedProgress");
|
||||
mProgressLabel = this.Q<Label>("Progress");
|
||||
mStatusLabel = this.Q<Label>("Status");
|
||||
mPercentageLabel = this.Q<Label>("Percentage");
|
||||
|
||||
mLoadingSpinner = new LoadingSpinner();
|
||||
mUndefinedProgress.Add(mLoadingSpinner);
|
||||
}
|
||||
|
||||
void InitializeLayoutAndStyles()
|
||||
{
|
||||
this.LoadLayout(typeof(ProgressControlsForDialogs).Name);
|
||||
|
||||
this.LoadStyle(typeof(ProgressControlsForDialogs).Name);
|
||||
}
|
||||
|
||||
static void CleanStatusMessage(Data data)
|
||||
{
|
||||
data.StatusMessage = string.Empty;
|
||||
data.StatusType = MessageType.None;
|
||||
}
|
||||
|
||||
Data mData = new Data();
|
||||
VisualElement mUndefinedProgress;
|
||||
Label mProgressLabel;
|
||||
Label mStatusLabel;
|
||||
Label mPercentageLabel;
|
||||
VisualElement[] mActionControls;
|
||||
|
||||
LoadingSpinner mLoadingSpinner;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f213e847b2dfa264ea3243bef42fbef0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,111 @@
|
||||
using System.IO;
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
using PlasticGui;
|
||||
using Unity.PlasticSCM.Editor.AssetUtils;
|
||||
|
||||
namespace Unity.PlasticSCM.Editor.UI.UIElements
|
||||
{
|
||||
internal static class UIElementsExtensions
|
||||
{
|
||||
internal static void LoadLayout(
|
||||
this VisualElement element,
|
||||
string className)
|
||||
{
|
||||
string uxmlRelativePath = Path.Combine(
|
||||
AssetsPath.GetLayoutsFolderRelativePath(),
|
||||
string.Format("{0}.uxml", className));
|
||||
|
||||
VisualTreeAsset visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
|
||||
uxmlRelativePath);
|
||||
|
||||
if (visualTree == null)
|
||||
{
|
||||
UnityEngine.Debug.LogErrorFormat(
|
||||
"Layout {0} not found at path {1}",
|
||||
className,
|
||||
uxmlRelativePath);
|
||||
return;
|
||||
}
|
||||
|
||||
visualTree.CloneTree(element);
|
||||
}
|
||||
|
||||
internal static void LoadStyle(
|
||||
this VisualElement element,
|
||||
string className)
|
||||
{
|
||||
string ussRelativePath = Path.Combine(
|
||||
AssetsPath.GetStylesFolderRelativePath(),
|
||||
string.Format("{0}.uss", className));
|
||||
|
||||
StyleSheet sheet = AssetDatabase.LoadAssetAtPath<StyleSheet>(
|
||||
ussRelativePath);
|
||||
|
||||
if (sheet != null)
|
||||
{
|
||||
element.styleSheets.Add(sheet);
|
||||
}
|
||||
|
||||
string ussSkinRelativePath = Path.Combine(
|
||||
AssetsPath.GetStylesFolderRelativePath(),
|
||||
string.Format("{0}.{1}.uss",
|
||||
className, EditorGUIUtility.isProSkin ? "dark" : "light"));
|
||||
|
||||
StyleSheet skinSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>(
|
||||
ussSkinRelativePath);
|
||||
|
||||
if (skinSheet == null)
|
||||
return;
|
||||
|
||||
element.styleSheets.Add(skinSheet);
|
||||
}
|
||||
|
||||
internal static void FocusOnceLoaded(
|
||||
this TextField elem)
|
||||
{
|
||||
// Really weird behavior from UIElements, apperently in order
|
||||
// to focus a text field you have to wait for it to attach to
|
||||
// the panel and then focus it's TextInputBaseField child
|
||||
// control rather than the TextField itself. For more see:
|
||||
// https://forum.unity.com/threads/focus-doesnt-seem-to-work.901130/
|
||||
elem.RegisterCallback<AttachToPanelEvent>(
|
||||
_ => elem.Q(TextInputBaseField<string>.textInputUssName).Focus());
|
||||
}
|
||||
|
||||
internal static void FocusWorkaround(this TextField textField)
|
||||
{
|
||||
// https://issuetracker.unity3d.com/issues/uielements-textfield-is-not-focused-and-you-are-not-able-to-type-in-characters-when-using-focus-method
|
||||
textField.Q("unity-text-input").Focus();
|
||||
}
|
||||
|
||||
internal static void SetControlImage(
|
||||
this VisualElement element,
|
||||
string name,
|
||||
Images.Name imageName)
|
||||
{
|
||||
Image imageElem = element.Query<Image>(name).First();
|
||||
imageElem.image = Images.GetImage(imageName);
|
||||
}
|
||||
|
||||
internal static void SetControlText<T>(
|
||||
this VisualElement element,
|
||||
string name, PlasticLocalization.Name fieldName,
|
||||
params string[] format) where T : VisualElement
|
||||
{
|
||||
dynamic control = element.Query<T>(name).First();
|
||||
string str = PlasticLocalization.GetString(fieldName);
|
||||
control.text = format.Length > 0 ? string.Format(str, format) : str;
|
||||
}
|
||||
|
||||
internal static void SetControlLabel<T>(
|
||||
this VisualElement element,
|
||||
string name, PlasticLocalization.Name fieldName) where T : VisualElement
|
||||
{
|
||||
dynamic control = element.Query<T>(name).First();
|
||||
control.label = PlasticLocalization.GetString(fieldName);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: daa34ff7d97bebd41866fd45e507c80e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user