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,290 @@
using System;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.UI.UIElements
{
internal static class Styles
{
internal static class Toolbar
{
internal static void ApplyImageButtonStyle(ToolbarButton button)
{
button.style.width = ImageButtonWidth;
button.style.justifyContent = Justify.Center;
button.style.alignItems = Align.Center;
button.style.paddingBottom = 1;
button.focusable = false;
}
internal static void ApplyButtonStyle(ToolbarButton button)
{
button.style.unityTextAlign = TextAnchor.MiddleCenter;
button.style.height = TextButtonHeight;
button.style.paddingBottom = 1;
button.focusable = false;
}
static StyleLength ImageButtonWidth = 28;
static StyleLength TextButtonHeight = 20;
}
internal static class Button
{
internal static void ApplyImageButtonStyle(UnityEngine.UIElements.Button button)
{
button.style.justifyContent = Justify.Center;
button.style.alignItems = Align.Center;
}
}
internal static class ButtonGroup
{
internal static void ApplyTopButtonStyle(UnityEngine.UIElements.Button button)
{
button.style.marginBottom = 0;
button.style.borderBottomLeftRadius = 0;
button.style.borderBottomRightRadius = 0;
}
internal static void ApplyBottomButtonStyle(UnityEngine.UIElements.Button button)
{
button.style.marginTop = 0;
button.style.borderTopWidth = 0;
button.style.borderTopLeftRadius = 0;
button.style.borderTopRightRadius = 0;
}
}
}
internal static class ControlBuilder
{
internal static class Toolbar
{
internal static UnityEditor.UIElements.Toolbar Create()
{
return new UnityEditor.UIElements.Toolbar();
}
internal static ToolbarSearchField CreateSearchField(string tooltip = null)
{
ToolbarSearchField searchField = new ToolbarSearchField();
searchField.tooltip = tooltip;
searchField.style.width = 200;
return searchField;
}
public static ToolbarButton CreateButtonLeft(string text, Action onClick)
{
ToolbarButton button = CreateButton(text, onClick);
// apply a negative margin to blend with previous button
button.style.marginRight = -1;
return button;
}
public static ToolbarButton CreateButton(string text, Action onClick)
{
ToolbarButton button = new ToolbarButton(onClick);
Styles.Toolbar.ApplyButtonStyle(button);
button.text = text;
return button;
}
internal static ToolbarButton CreateImageButtonLeft(
Texture image,
string tooltip,
Action onClick)
{
ToolbarButton button = CreateImageButton(
image,
tooltip,
onClick);
// apply a negative margin to blend with next button
button.style.marginRight = -1;
return button;
}
internal static ToolbarButton CreateImageButton(
Texture image,
string tooltip,
Action onClick)
{
var button = new ToolbarButton(onClick)
{
tooltip = tooltip,
};
var icon = new Image
{
image = image,
};
Styles.Toolbar.ApplyImageButtonStyle(button);
button.Add(icon);
return button;
}
internal static ToolbarToggle CreateToggleLeft(
string text,
string tooltip)
{
ToolbarToggle toggle = CreateToggle(text, tooltip);
// apply a negative margin to blend with next button
toggle.style.marginRight = -1;
return toggle;
}
internal static ToolbarToggle CreateToggle(
string text,
string tooltip)
{
ToolbarToggle toggle = new ToolbarToggle();
toggle.text = text;
toggle.tooltip = tooltip;
toggle.style.unityTextAlign = TextAnchor.MiddleCenter;
return toggle;
}
internal static MultiSelectDropdown CreateMultiSelectDropdownLeft(
string labelPrefix)
{
MultiSelectDropdown dropdown = CreateMultiSelectDropdown(labelPrefix);
// apply a negative margin to blend with next button
dropdown.style.marginRight = -1;
return dropdown;
}
internal static MultiSelectDropdown CreateMultiSelectDropdown(
string labelPrefix)
{
return new MultiSelectDropdown(labelPrefix);
}
internal static ToolbarToggle CreateImageToggleLeft(
Texture image,
string tooltip)
{
ToolbarToggle toggle = CreateImageToggle(image, tooltip);
toggle.style.marginRight = -1;
return toggle;
}
internal static ToolbarToggle CreateImageToggle(
Texture image,
string tooltip)
{
ToolbarToggle toggle = new ToolbarToggle();
toggle.tooltip = tooltip;
Image icon = new Image();
icon.image = image;
toggle.Add(icon);
return toggle;
}
}
internal static class Button
{
internal static UnityEngine.UIElements.Button CreateImageButton(
Texture image,
string tooltip,
System.Action onClick)
{
UnityEngine.UIElements.Button button = new UnityEngine.UIElements.Button(onClick)
{
tooltip = tooltip,
focusable = false
};
Image icon = new Image()
{
image = image,
};
Styles.Button.ApplyImageButtonStyle(button);
button.Add(icon);
return button;
}
public static UnityEngine.UIElements.Button CreateButton(string text, Action handler)
{
UnityEngine.UIElements.Button result = new UnityEngine.UIElements.Button(handler);
result.text = text;
return result;
}
public static UnityEngine.UIElements.Button CreateDropDownButton(string text, Action handler)
{
UnityEngine.UIElements.Button button = new UnityEngine.UIElements.Button(handler);
button.style.flexDirection = FlexDirection.Row;
TextElement textElement = new TextElement();
textElement.text = text;
VisualElement dropDownIcon = new VisualElement();
dropDownIcon.AddToClassList("unity-base-popup-field__arrow");
dropDownIcon.style.marginLeft = 4;
button.Add(textElement);
button.Add(dropDownIcon);
return button;
}
}
internal static class ButtonGroup
{
internal static UnityEngine.UIElements.Button CreateImageTopButton(
Texture image,
string tooltip,
System.Action onClick)
{
UnityEngine.UIElements.Button button =
Button.CreateImageButton(image, tooltip, onClick);
Styles.ButtonGroup.ApplyTopButtonStyle(button);
return button;
}
internal static UnityEngine.UIElements.Button CreateImageBottomButton(
Texture image,
string tooltip,
System.Action onClick)
{
UnityEngine.UIElements.Button button =
Button.CreateImageButton(image, tooltip, onClick);
Styles.ButtonGroup.ApplyBottomButtonStyle(button);
return button;
}
}
internal static class Label
{
internal static UnityEngine.UIElements.Label CreateSelectableLabel(string text = null)
{
UnityEngine.UIElements.Label result = new UnityEngine.UIElements.Label();
result.selection.isSelectable = true;
result.SetMouseCursor(MouseCursor.Text);
result.text = text;
return result;
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b01e8ea6bc7b48c38aa343ab609290fc
timeCreated: 1768302646
@@ -0,0 +1,39 @@
using UnityEditor;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.UI.UIElements
{
internal static class CursorExtensions
{
internal static void SetMouseCursor(this VisualElement visualElement, MouseCursor mouseCursor)
{
var cursor = new Cursor();
var prop = typeof(Cursor).GetProperty("defaultCursorId",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
if (prop != null)
{
object boxed = cursor;
prop.SetValue(boxed, (int)mouseCursor);
cursor = (Cursor)boxed;
}
visualElement.style.cursor = new StyleCursor(cursor);
}
internal static void SetTextCursor(this TextField textField)
{
var textInput = textField.Q("unity-text-input");
if (textInput == null)
return;
textInput.SetMouseCursor(MouseCursor.Text);
var textElement = textInput.Q<TextElement>();
if (textElement == null)
return;
textElement.SetMouseCursor(MouseCursor.Text);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 92adf24130b54e35b6b5830788e8150a
timeCreated: 1773390184
@@ -0,0 +1,108 @@
using Codice.Utils;
using UnityEngine;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.UI.UIElements
{
internal static class KeyboardEvents
{
internal static bool MatchesShortcut(KeyDownEvent e, string shortcut)
{
if (string.IsNullOrEmpty(shortcut))
return false;
bool needCtrl = false;
bool needCmd = false;
bool needShift = false;
bool needAlt = false;
int keyIndex = 0;
while (keyIndex < shortcut.Length)
{
switch (shortcut[keyIndex])
{
case '%':
if (PlatformIdentifier.IsMac())
needCmd = true;
else
needCtrl = true;
break;
case '^':
needCtrl = true;
break;
case '#':
needShift = true;
break;
case '&':
needAlt = true;
break;
case '_':
break;
default:
goto doneParsingModifiers;
}
keyIndex++;
}
doneParsingModifiers:
if (keyIndex >= shortcut.Length)
return false;
string keyStr = shortcut.Substring(keyIndex);
if (keyStr.Length != 1)
return false;
KeyCode expectedKey = (KeyCode)char.ToLower(keyStr[0]);
return e.keyCode == expectedKey
&& e.ctrlKey == needCtrl
&& e.commandKey == needCmd
&& e.shiftKey == needShift
&& e.altKey == needAlt;
}
internal static bool IsAltPressed(KeyDownEvent evt)
{
return evt.altKey;
}
internal static bool IsControlPressed(KeyDownEvent evt)
{
return evt.ctrlKey;
}
internal static bool IsCommandPressed(KeyDownEvent evt)
{
return evt.commandKey;
}
internal static bool IsShiftPressed(KeyDownEvent evt)
{
return evt.shiftKey;
}
internal static bool IsEscapePressed(KeyDownEvent evt)
{
return evt.keyCode == KeyCode.Escape;
}
internal static bool IsEnterPressed(KeyDownEvent evt)
{
return evt.keyCode == KeyCode.Return ||
evt.keyCode == KeyCode.KeypadEnter;
}
internal static bool IsFindShortcutPressed(KeyDownEvent evt)
{
if (evt.keyCode != KeyCode.F)
return false;
if (PlatformIdentifier.IsMac())
return evt.commandKey;
return evt.ctrlKey;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 59491e42fa7743e193e65d5c778cd8b2
timeCreated: 1769417114
@@ -0,0 +1,64 @@
using UnityEditor;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.UI.UIElements
{
internal class LoadingSpinner : VisualElement
{
internal LoadingSpinner()
{
// 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 Start()
{
if (mRotationEvent != null)
return;
mRotationEvent = mSpinner.schedule.Execute(UpdateProgress).Every(ROTATION_REFRESH_RATE);
mStartTime = EditorApplication.timeSinceStartup;
}
internal void Stop()
{
if (mRotationEvent == null)
return;
mRotationEvent.Pause();
mRotationEvent = null;
}
void UpdateProgress()
{
double elapsedTime = EditorApplication.timeSinceStartup - mStartTime;
int rotation = (int)(ROTATION_SPEED * elapsedTime) % 360;
mSpinnerStyleRotate.value = new Rotate(rotation);
mSpinner.style.rotate = mSpinnerStyleRotate;
}
double mStartTime;
VisualElement mSpinner;
IVisualElementScheduledItem mRotationEvent;
StyleRotate mSpinnerStyleRotate;
const int ROTATION_SPEED = 360; // Euler degrees per second
const int ROTATION_REFRESH_RATE = 32; // (ms) roughly 30 FPS
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4664c2d6dd034f04eb60d4190ee2ffb7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,17 @@
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.UI.UIElements
{
internal static class MouseEvents
{
internal static bool IsRightButtonPressed(PointerDownEvent evt)
{
return IsRightButtonPressed(evt.button);
}
internal static bool IsRightButtonPressed(int button)
{
return button == UnityConstants.RIGHT_MOUSE_BUTTON;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 317c87f583644be895fb6679dd000910
timeCreated: 1769601856
@@ -0,0 +1,497 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
using PlasticGui;
namespace Unity.PlasticSCM.Editor.UI.UIElements
{
internal class MultiSelectDropdownItem
{
internal readonly string Name;
internal readonly Texture Icon;
internal MultiSelectDropdownItem(string name, Texture icon)
{
Name = name;
Icon = icon;
}
}
internal class MultiSelectDropdown : VisualElement
{
internal event Action SelectionChanged;
internal MultiSelectDropdown(string labelPrefix)
{
mLabelPrefix = labelPrefix;
mLabelElement = new TextElement();
mLabelElement.style.unityTextAlign = TextAnchor.MiddleLeft;
mLabelElement.style.flexGrow = 1;
VisualElement chevron = new VisualElement();
chevron.AddToClassList("unity-base-popup-field__arrow");
chevron.style.marginLeft = 4;
mButton = new ToolbarButton(OnButtonClick);
mButton.style.flexDirection = FlexDirection.Row;
mButton.style.unityTextAlign = TextAnchor.MiddleLeft;
mButton.style.height = 20;
mButton.style.paddingBottom = 1;
mButton.focusable = false;
mButton.Add(mLabelElement);
mButton.Add(chevron);
Add(mButton);
UpdateLabel();
UpdateEnabledState();
}
internal void SetItems(IList<MultiSelectDropdownItem> items)
{
mItems = items ?? new List<MultiSelectDropdownItem>();
UpdateLabel();
UpdateEnabledState();
}
internal HashSet<string> Selected
{
get { return mSelected; }
set
{
mSelected = value;
UpdateLabel();
}
}
void OnButtonClick()
{
if (mItems.Count == 0)
return;
MultiSelectDropdownPopupContent content = new MultiSelectDropdownPopupContent(
mItems, mSelected, OnPopupSelectionChanged);
UnityEditor.PopupWindow.Show(mButton.worldBound, content);
}
void OnPopupSelectionChanged(HashSet<string> newSelected)
{
mSelected = newSelected;
UpdateLabel();
if (SelectionChanged != null)
SelectionChanged();
}
void UpdateLabel()
{
mLabelElement.text = BuildLabel();
}
string BuildLabel()
{
if (mSelected == null)
return mLabelPrefix;
string firstSelected;
int totalSelected;
CountVisibleSelected(out firstSelected, out totalSelected);
if (totalSelected == 0)
return string.Format("{0}: {1}",
mLabelPrefix,
PlasticLocalization.Name.MultiSelectNoneSelected.GetString());
if (totalSelected == 1)
return string.Format("{0}: {1}", mLabelPrefix, firstSelected);
return string.Format("{0}: {1}",
mLabelPrefix,
PlasticLocalization.Name.MultiSelectOverflowFormat.GetString(
firstSelected, totalSelected - 1));
}
void CountVisibleSelected(out string firstSelected, out int totalSelected)
{
firstSelected = null;
totalSelected = 0;
foreach (MultiSelectDropdownItem item in mItems)
{
if (!mSelected.Contains(item.Name))
continue;
if (firstSelected == null)
firstSelected = item.Name;
totalSelected++;
}
}
void UpdateEnabledState()
{
mButton.SetEnabled(mItems.Count > 0);
}
readonly string mLabelPrefix;
readonly TextElement mLabelElement;
readonly ToolbarButton mButton;
IList<MultiSelectDropdownItem> mItems = new List<MultiSelectDropdownItem>();
HashSet<string> mSelected;
}
internal class MultiSelectDropdownPopupContent : PopupWindowContent
{
internal MultiSelectDropdownPopupContent(
IList<MultiSelectDropdownItem> items,
HashSet<string> currentSelection,
Action<HashSet<string>> onSelectionChanged)
{
mAllItems = items;
mFilteredItems = new List<MultiSelectDropdownItem>(items);
mSelected = currentSelection == null
? null
: new HashSet<string>(currentSelection);
mOnSelectionChanged = onSelectionChanged;
}
public override Vector2 GetWindowSize()
{
int itemCount = mAllItems == null ? 0 : mAllItems.Count;
float contentHeight = SEARCH_AREA_HEIGHT
+ (itemCount * ROW_HEIGHT)
+ FOOTER_HEIGHT;
float height = Mathf.Clamp(contentHeight, POPUP_HEIGHT_MIN, POPUP_HEIGHT_MAX);
return new Vector2(POPUP_WIDTH, height);
}
public override void OnOpen()
{
BuildGUI(editorWindow.rootVisualElement);
}
public override void OnGUI(Rect rect)
{
if (Event.current.type != EventType.KeyDown)
return;
if (Event.current.keyCode != KeyCode.Escape)
return;
editorWindow.Close();
Event.current.Use();
}
void BuildGUI(VisualElement root)
{
root.style.flexDirection = FlexDirection.Column;
ToolbarSearchField searchField = new ToolbarSearchField();
searchField.style.width = POPUP_WIDTH - 2 * SEARCH_HORIZONTAL_MARGIN;
searchField.style.marginTop = SEARCH_HORIZONTAL_MARGIN;
searchField.style.marginLeft = SEARCH_HORIZONTAL_MARGIN;
searchField.style.marginRight = SEARCH_HORIZONTAL_MARGIN;
searchField.style.marginBottom = SEARCH_HORIZONTAL_MARGIN;
searchField.RegisterValueChangedCallback(OnSearchChanged);
root.Add(searchField);
mListView = new ListView();
mListView.itemsSource = mFilteredItems;
mListView.fixedItemHeight = ROW_HEIGHT;
mListView.makeItem = MakeRow;
mListView.bindItem = BindRow;
mListView.selectionType = SelectionType.None;
mListView.style.flexGrow = 1;
mListView.style.minHeight = 0;
root.Add(mListView);
VisualElement footer = new VisualElement();
footer.style.flexDirection = FlexDirection.Row;
footer.style.justifyContent = Justify.FlexEnd;
footer.style.flexShrink = 0;
footer.style.paddingLeft = 4;
footer.style.paddingRight = 4;
footer.style.paddingBottom = 4;
footer.style.paddingTop = 4;
footer.style.borderTopWidth = 1;
footer.style.borderTopColor = UnityStyles.Colors.BarBorder;
Button resetButton = new Button(OnResetClick);
resetButton.text = PlasticLocalization.Name.MultiSelectResetButton.GetString();
resetButton.style.fontSize = ROW_FONT_SIZE;
resetButton.style.minWidth = 60;
footer.Add(resetButton);
root.Add(footer);
}
VisualElement MakeRow()
{
VisualElement row = new VisualElement();
row.style.flexDirection = FlexDirection.Row;
row.style.alignItems = Align.Center;
row.style.paddingLeft = ROW_HORIZONTAL_PADDING;
row.style.paddingRight = ROW_HORIZONTAL_PADDING;
row.style.height = ROW_HEIGHT;
Image icon = new Image();
icon.name = ROW_ICON_NAME;
icon.style.width = ICON_SIZE;
icon.style.height = ICON_SIZE;
icon.style.marginRight = 4;
icon.scaleMode = ScaleMode.ScaleToFit;
row.Add(icon);
Label label = new Label();
label.name = ROW_LABEL_NAME;
label.style.flexGrow = 1;
label.style.fontSize = ROW_FONT_SIZE;
label.style.unityTextAlign = TextAnchor.MiddleLeft;
label.style.overflow = Overflow.Hidden;
label.style.textOverflow = TextOverflow.Ellipsis;
label.style.marginLeft = 0;
label.style.marginRight = 0;
label.style.paddingLeft = 0;
label.style.paddingRight = 0;
row.Add(label);
Button hoverLink = new Button();
hoverLink.name = ROW_HOVER_LINK_NAME;
hoverLink.style.color = UnityStyles.Colors.Link;
hoverLink.style.fontSize = ROW_FONT_SIZE;
hoverLink.style.marginLeft = 6;
hoverLink.style.marginRight = 6;
hoverLink.style.paddingLeft = 0;
hoverLink.style.paddingRight = 0;
hoverLink.style.borderTopWidth = 0;
hoverLink.style.borderBottomWidth = 0;
hoverLink.style.borderLeftWidth = 0;
hoverLink.style.borderRightWidth = 0;
hoverLink.style.backgroundColor = Color.clear;
hoverLink.style.display = DisplayStyle.None;
hoverLink.SetMouseCursor(MouseCursor.Link);
hoverLink.clicked += () => OnHoverLinkClick(hoverLink);
row.Add(hoverLink);
Toggle checkbox = new Toggle();
checkbox.name = ROW_CHECKBOX_NAME;
checkbox.style.marginLeft = 0;
checkbox.style.marginRight = 0;
checkbox.RegisterValueChangedCallback(OnCheckboxChanged);
row.Add(checkbox);
row.RegisterCallback<MouseEnterEvent>(OnRowMouseEnter);
row.RegisterCallback<MouseLeaveEvent>(OnRowMouseLeave);
return row;
}
void BindRow(VisualElement element, int index)
{
if (index < 0 || index >= mFilteredItems.Count)
return;
MultiSelectDropdownItem item = mFilteredItems[index];
Image icon = element.Q<Image>(ROW_ICON_NAME);
Label label = element.Q<Label>(ROW_LABEL_NAME);
Toggle checkbox = element.Q<Toggle>(ROW_CHECKBOX_NAME);
Button hoverLink = element.Q<Button>(ROW_HOVER_LINK_NAME);
icon.image = item.Icon;
icon.style.display = item.Icon != null ? DisplayStyle.Flex : DisplayStyle.None;
label.text = item.Name;
bool isChecked = IsItemSelected(item.Name);
checkbox.SetValueWithoutNotify(isChecked);
checkbox.userData = item.Name;
hoverLink.userData = item.Name;
UpdateHoverLink(hoverLink, item.Name);
}
void UpdateHoverLink(Button hoverLink, string itemName)
{
if (mHoveredItem != itemName)
{
hoverLink.style.display = DisplayStyle.None;
return;
}
hoverLink.text = IsOnlySelected(itemName)
? PlasticLocalization.Name.MultiSelectSelectAllAction.GetString()
: PlasticLocalization.Name.MultiSelectOnlyAction.GetString();
hoverLink.style.display = DisplayStyle.Flex;
}
bool IsItemSelected(string itemName)
{
return mSelected == null || mSelected.Contains(itemName);
}
void OnRowMouseEnter(MouseEnterEvent evt)
{
VisualElement row = (VisualElement)evt.currentTarget;
Button hoverLink = row.Q<Button>(ROW_HOVER_LINK_NAME);
mHoveredItem = (string)hoverLink.userData;
row.style.backgroundColor = UnityStyles.Colors.HoverBackgroundColor;
UpdateHoverLink(hoverLink, mHoveredItem);
}
void OnRowMouseLeave(MouseLeaveEvent evt)
{
VisualElement row = (VisualElement)evt.currentTarget;
Button hoverLink = row.Q<Button>(ROW_HOVER_LINK_NAME);
string itemName = (string)hoverLink.userData;
if (mHoveredItem == itemName)
mHoveredItem = null;
row.style.backgroundColor = StyleKeyword.Null;
hoverLink.style.display = DisplayStyle.None;
}
void OnHoverLinkClick(Button hoverLink)
{
string itemName = (string)hoverLink.userData;
if (IsOnlySelected(itemName))
{
mSelected = null;
}
else
{
mSelected = new HashSet<string>();
mSelected.Add(itemName);
}
FireSelectionChanged();
mListView.RefreshItems();
}
void OnCheckboxChanged(ChangeEvent<bool> evt)
{
Toggle checkbox = (Toggle)evt.currentTarget;
string itemName = (string)checkbox.userData;
if (mSelected == null)
{
mSelected = new HashSet<string>();
foreach (MultiSelectDropdownItem item in mAllItems)
mSelected.Add(item.Name);
}
if (evt.newValue)
mSelected.Add(itemName);
else
mSelected.Remove(itemName);
if (IsAllSelected())
mSelected = null;
FireSelectionChanged();
}
bool IsAllSelected()
{
if (mSelected == null)
return true;
if (mSelected.Count != mAllItems.Count)
return false;
foreach (MultiSelectDropdownItem item in mAllItems)
{
if (!mSelected.Contains(item.Name))
return false;
}
return true;
}
void OnSearchChanged(ChangeEvent<string> evt)
{
ApplySearchFilter(evt.newValue);
}
void ApplySearchFilter(string query)
{
mFilteredItems.Clear();
if (string.IsNullOrEmpty(query))
{
foreach (MultiSelectDropdownItem item in mAllItems)
mFilteredItems.Add(item);
}
else
{
string lower = query.ToLowerInvariant();
foreach (MultiSelectDropdownItem item in mAllItems)
{
if (item.Name.ToLowerInvariant().Contains(lower))
mFilteredItems.Add(item);
}
}
mListView.itemsSource = mFilteredItems;
mListView.Rebuild();
}
void OnResetClick()
{
mSelected = null;
FireSelectionChanged();
mListView.RefreshItems();
}
bool IsOnlySelected(string itemName)
{
return mSelected != null
&& mSelected.Count == 1
&& mSelected.Contains(itemName);
}
void FireSelectionChanged()
{
if (mOnSelectionChanged == null)
return;
HashSet<string> snapshot = mSelected == null
? null
: new HashSet<string>(mSelected);
mOnSelectionChanged(snapshot);
}
readonly IList<MultiSelectDropdownItem> mAllItems;
readonly List<MultiSelectDropdownItem> mFilteredItems;
readonly Action<HashSet<string>> mOnSelectionChanged;
HashSet<string> mSelected;
string mHoveredItem;
ListView mListView;
const float POPUP_WIDTH = 280f;
const float POPUP_HEIGHT_MIN = 80f;
const float POPUP_HEIGHT_MAX = 360f;
const float SEARCH_AREA_HEIGHT = 28f;
const float FOOTER_HEIGHT = 32f;
const int ROW_HEIGHT = 18;
const int ROW_HORIZONTAL_PADDING = 4;
const int ROW_FONT_SIZE = 12;
const int ICON_SIZE = 14;
const int SEARCH_HORIZONTAL_MARGIN = 4;
const string ROW_ICON_NAME = "icon";
const string ROW_LABEL_NAME = "label";
const string ROW_HOVER_LINK_NAME = "hoverLink";
const string ROW_CHECKBOX_NAME = "checkbox";
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2d525c6ef37834b1983617208d2b60ae
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,165 @@
using System;
using System.Threading.Tasks;
using PlasticGui;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.UI.UIElements
{
internal class OverlayProgressControls : VisualElement, IProgressControls
{
internal OverlayProgressControls(params VisualElement[] actionControls)
{
mActionControls = actionControls;
InitializeLayoutAndStyles();
BuildComponents();
visible = false;
}
internal Task ShowProgress(string message, TimeSpan after)
{
lock (mLock)
{
mIsOperationRunning = true;
mCurrentOperation = new object();
}
return Task.Delay(after).ContinueWith((task, state) =>
{
EditorDispatcher.Dispatch(() =>
{
lock (mLock)
{
if (!mIsOperationRunning)
return;
if (state != mCurrentOperation)
return;
}
((IProgressControls)this).ShowProgress(message);
});
}, mCurrentOperation);
}
void EnableActionControls(bool enable)
{
foreach (var control in mActionControls)
if (control != null)
control.SetEnabled(enable);
}
void IProgressControls.ShowProgress(string message)
{
lock (mLock)
{
mIsOperationRunning = true;
mProgressBar.title = message;
if (visible)
return;
visible = true;
EnableActionControls(false);
mLastUpdateTime = EditorApplication.timeSinceStartup;
mTimer = schedule.Execute(UpdateProgress).Every(16);
}
}
void IProgressControls.HideProgress()
{
lock (mLock)
{
mIsOperationRunning = false;
if (!visible)
return;
visible = false;
EnableActionControls(true);
mTimer.Pause();
mTimer = null;
}
}
void IProgressControls.ShowNotification(string message)
{
throw new System.NotImplementedException();
}
void IProgressControls.ShowError(string message)
{
throw new System.NotImplementedException();
}
void IProgressControls.ShowWarning(string message)
{
throw new System.NotImplementedException();
}
void IProgressControls.ShowSuccess(string message)
{
throw new System.NotImplementedException();
}
void UpdateProgress()
{
double now = EditorApplication.timeSinceStartup;
double deltaTime = now - mLastUpdateTime;
mLastUpdateTime = now;
double deltaPercent = deltaTime * PERCENT_PER_SECONDS;
mProgressBar.value = Mathf.Repeat(mProgressBar.value + (float)deltaPercent, 1f);
}
void InitializeLayoutAndStyles()
{
style.position = Position.Absolute;
style.top = 0;
style.left = 0;
style.bottom = 0;
style.right = 0;
style.justifyContent = Justify.Center;
style.alignContent = Align.Center;
style.backgroundColor = UnityStyles.Colors.OverlayProgressBackgroundColor;
}
void BuildComponents()
{
mProgressBar = new ProgressBar();
mProgressBar.lowValue = 0;
mProgressBar.highValue = 1;
mProgressBar.style.alignSelf = Align.Center;
Label progressBarLabel = mProgressBar.Q<Label>();
progressBarLabel.style.marginLeft = 30;
progressBarLabel.style.marginRight = 30;
progressBarLabel.style.textOverflow = TextOverflow.Ellipsis;
progressBarLabel.style.overflow = Overflow.Hidden;
progressBarLabel.style.unityTextOverflowPosition = TextOverflowPosition.End;
Add(mProgressBar);
}
double mLastUpdateTime;
const double PERCENT_PER_SECONDS = 0.6;
object mLock = new object();
object mCurrentOperation = new object();
bool mIsOperationRunning;
IVisualElementScheduledItem mTimer;
ProgressBar mProgressBar;
readonly VisualElement[] mActionControls;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0288f47d0c1f4195b1fdf2c90407c9c3
timeCreated: 1768912199
@@ -0,0 +1,184 @@
using UnityEditor;
using UnityEngine.UIElements;
using Codice.Client.Common.Authentication;
using PlasticGui;
namespace Unity.PlasticSCM.Editor.UI.UIElements
{
class ProgressControlsForDialogs :
VisualElement,
IProgressControls,
IAuthenticationProgressControls
{
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 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()
{
InternalHideProgress();
}
void IAuthenticationProgressControls.HideProgress()
{
InternalHideProgress();
}
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;
}
void InternalHideProgress()
{
EnableActionControls(true);
mData.IsWaitingAsyncResult = false;
mData.ProgressMessage = string.Empty;
ForcedUpdateProgress();
}
void IProgressControls.ShowProgress(string message)
{
InternalShowProgress(message);
}
void IAuthenticationProgressControls.ShowProgress(string message)
{
InternalShowProgress(message);
}
void InternalShowProgress(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,125 @@
using System.IO;
using UnityEditor;
using UnityEngine;
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);
}
internal static Event ToIMGUIEvent(this KeyDownEvent keyDownEvent)
{
var e = new Event
{
type = EventType.KeyDown,
keyCode = keyDownEvent.keyCode,
character = keyDownEvent.character,
modifiers = keyDownEvent.modifiers
};
return e;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: daa34ff7d97bebd41866fd45e507c80e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: