Level save + load
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace SimpleFileBrowser
|
||||
{
|
||||
// Avoid multiple EventSystems in the scene by activating the embedded EventSystem only if one doesn't already exist in the scene
|
||||
[DefaultExecutionOrder( 1000 )]
|
||||
public class EventSystemHandler : MonoBehaviour
|
||||
{
|
||||
#pragma warning disable 0649
|
||||
[SerializeField]
|
||||
private GameObject embeddedEventSystem;
|
||||
#pragma warning restore 0649
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
SceneManager.sceneUnloaded -= OnSceneUnloaded;
|
||||
SceneManager.sceneUnloaded += OnSceneUnloaded;
|
||||
|
||||
ActivateEventSystemIfNeeded();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
SceneManager.sceneUnloaded -= OnSceneUnloaded;
|
||||
|
||||
DeactivateEventSystem();
|
||||
}
|
||||
|
||||
private void OnSceneLoaded( Scene scene, LoadSceneMode mode )
|
||||
{
|
||||
#if UNITY_2017_2_OR_NEWER
|
||||
DeactivateEventSystem();
|
||||
#endif
|
||||
ActivateEventSystemIfNeeded();
|
||||
}
|
||||
|
||||
private void OnSceneUnloaded( Scene current )
|
||||
{
|
||||
// Deactivate the embedded EventSystem before changing scenes because the new scene might have its own EventSystem
|
||||
DeactivateEventSystem();
|
||||
}
|
||||
|
||||
private void ActivateEventSystemIfNeeded()
|
||||
{
|
||||
if( embeddedEventSystem && !EventSystem.current )
|
||||
embeddedEventSystem.SetActive( true );
|
||||
}
|
||||
|
||||
private void DeactivateEventSystem()
|
||||
{
|
||||
if( embeddedEventSystem )
|
||||
embeddedEventSystem.SetActive( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0868341868a4a4641b4d272d2fc5f538
|
||||
timeCreated: 1658741613
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3149
Assets/Plugins/SimpleFileBrowser/Scripts/FileBrowser.cs
Normal file
3149
Assets/Plugins/SimpleFileBrowser/Scripts/FileBrowser.cs
Normal file
File diff suppressed because it is too large
Load Diff
12
Assets/Plugins/SimpleFileBrowser/Scripts/FileBrowser.cs.meta
Normal file
12
Assets/Plugins/SimpleFileBrowser/Scripts/FileBrowser.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f51dc09bf9e35804ba0f5e76c527025e
|
||||
timeCreated: 1479416382
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
||||
using UnityEngine.InputSystem;
|
||||
#endif
|
||||
|
||||
namespace SimpleFileBrowser
|
||||
{
|
||||
public class FileBrowserAccessRestrictedPanel : MonoBehaviour
|
||||
{
|
||||
#pragma warning disable 0649
|
||||
[SerializeField]
|
||||
private Text messageLabel;
|
||||
|
||||
[SerializeField]
|
||||
private Button okButton;
|
||||
#pragma warning restore 0649
|
||||
|
||||
internal void Show()
|
||||
{
|
||||
gameObject.SetActive( true );
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WSA || UNITY_WSA_10_0
|
||||
private void LateUpdate()
|
||||
{
|
||||
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
||||
if( Keyboard.current != null )
|
||||
#endif
|
||||
{
|
||||
// Handle keyboard shortcuts
|
||||
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
||||
if( Keyboard.current[Key.Enter].wasPressedThisFrame || Keyboard.current[Key.NumpadEnter].wasPressedThisFrame || Keyboard.current[Key.Escape].wasPressedThisFrame )
|
||||
#else
|
||||
if( Input.GetKeyDown( KeyCode.Return ) || Input.GetKeyDown( KeyCode.KeypadEnter ) || Input.GetKeyDown( KeyCode.Escape ) )
|
||||
#endif
|
||||
OKButtonClicked();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
internal void RefreshSkin( UISkin skin )
|
||||
{
|
||||
Image background = GetComponentInChildren<Image>();
|
||||
background.color = skin.PopupPanelsBackgroundColor;
|
||||
background.sprite = skin.PopupPanelsBackground;
|
||||
|
||||
skin.ApplyTo( okButton );
|
||||
skin.ApplyTo( messageLabel, skin.PopupPanelsTextColor );
|
||||
}
|
||||
|
||||
public void OKButtonClicked()
|
||||
{
|
||||
gameObject.SetActive( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 85ea21be7cacb484cb6db0d183d3b2a8
|
||||
timeCreated: 1603800894
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,142 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace SimpleFileBrowser
|
||||
{
|
||||
public class FileBrowserContextMenu : MonoBehaviour
|
||||
{
|
||||
#pragma warning disable 0649
|
||||
[SerializeField]
|
||||
private FileBrowser fileBrowser;
|
||||
|
||||
[SerializeField]
|
||||
private RectTransform rectTransform;
|
||||
|
||||
[SerializeField]
|
||||
private Button selectAllButton;
|
||||
[SerializeField]
|
||||
private Button deselectAllButton;
|
||||
[SerializeField]
|
||||
private Button createFolderButton;
|
||||
[SerializeField]
|
||||
private Button deleteButton;
|
||||
[SerializeField]
|
||||
private Button renameButton;
|
||||
|
||||
[SerializeField]
|
||||
private GameObject selectAllButtonSeparator;
|
||||
|
||||
[SerializeField]
|
||||
private Text[] allButtonTexts;
|
||||
[SerializeField]
|
||||
private Image[] allButtonSeparators;
|
||||
|
||||
[SerializeField]
|
||||
private float minDistanceToEdges = 10f;
|
||||
#pragma warning restore 0649
|
||||
|
||||
internal void Show( bool selectAllButtonVisible, bool deselectAllButtonVisible, bool deleteButtonVisible, bool renameButtonVisible, Vector2 position, bool isMoreOptionsMenu )
|
||||
{
|
||||
selectAllButton.gameObject.SetActive( selectAllButtonVisible );
|
||||
deselectAllButton.gameObject.SetActive( deselectAllButtonVisible );
|
||||
deleteButton.gameObject.SetActive( deleteButtonVisible );
|
||||
renameButton.gameObject.SetActive( renameButtonVisible );
|
||||
selectAllButtonSeparator.SetActive( !deselectAllButtonVisible );
|
||||
|
||||
rectTransform.anchoredPosition = position;
|
||||
gameObject.SetActive( true );
|
||||
|
||||
if( isMoreOptionsMenu )
|
||||
rectTransform.pivot = Vector2.one;
|
||||
else
|
||||
{
|
||||
// Find the optimal pivot value
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate( rectTransform );
|
||||
|
||||
Vector2 size = rectTransform.sizeDelta;
|
||||
Vector2 canvasSize = fileBrowser.rectTransform.sizeDelta;
|
||||
|
||||
// Take canvas' Pivot into consideration
|
||||
Vector2 positionOffset = canvasSize;
|
||||
positionOffset.Scale( fileBrowser.rectTransform.pivot );
|
||||
position += positionOffset;
|
||||
|
||||
// Try bottom-right corner first
|
||||
Vector2 cornerPos = position + new Vector2( size.x + minDistanceToEdges, -size.y - minDistanceToEdges );
|
||||
if( cornerPos.x <= canvasSize.x && cornerPos.y >= 0f )
|
||||
rectTransform.pivot = new Vector2( 0f, 1f );
|
||||
else
|
||||
{
|
||||
// Try bottom-left corner
|
||||
cornerPos = position - new Vector2( size.x + minDistanceToEdges, size.y + minDistanceToEdges );
|
||||
if( cornerPos.x >= 0f && cornerPos.y >= 0f )
|
||||
rectTransform.pivot = Vector2.one;
|
||||
else
|
||||
{
|
||||
// Try top-right corner
|
||||
cornerPos = position + new Vector2( size.x + minDistanceToEdges, size.y + minDistanceToEdges );
|
||||
if( cornerPos.x <= canvasSize.x && cornerPos.y <= canvasSize.y )
|
||||
rectTransform.pivot = Vector2.zero;
|
||||
else
|
||||
{
|
||||
// Use top-left corner
|
||||
rectTransform.pivot = new Vector2( 1f, 0f );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void Hide()
|
||||
{
|
||||
gameObject.SetActive( false );
|
||||
}
|
||||
|
||||
internal void RefreshSkin( UISkin skin )
|
||||
{
|
||||
rectTransform.GetComponent<Image>().color = skin.ContextMenuBackgroundColor;
|
||||
|
||||
deselectAllButton.image.color = skin.ContextMenuBackgroundColor;
|
||||
selectAllButton.image.color = skin.ContextMenuBackgroundColor;
|
||||
createFolderButton.image.color = skin.ContextMenuBackgroundColor;
|
||||
deleteButton.image.color = skin.ContextMenuBackgroundColor;
|
||||
renameButton.image.color = skin.ContextMenuBackgroundColor;
|
||||
|
||||
for( int i = 0; i < allButtonTexts.Length; i++ )
|
||||
skin.ApplyTo( allButtonTexts[i], skin.ContextMenuTextColor );
|
||||
|
||||
for( int i = 0; i < allButtonSeparators.Length; i++ )
|
||||
allButtonSeparators[i].color = skin.ContextMenuSeparatorColor;
|
||||
}
|
||||
|
||||
public void OnSelectAllButtonClicked()
|
||||
{
|
||||
Hide();
|
||||
fileBrowser.SelectAllFiles();
|
||||
}
|
||||
|
||||
public void OnDeselectAllButtonClicked()
|
||||
{
|
||||
Hide();
|
||||
fileBrowser.DeselectAllFiles();
|
||||
}
|
||||
|
||||
public void OnCreateFolderButtonClicked()
|
||||
{
|
||||
Hide();
|
||||
fileBrowser.CreateNewFolder();
|
||||
}
|
||||
|
||||
public void OnDeleteButtonClicked()
|
||||
{
|
||||
Hide();
|
||||
fileBrowser.DeleteSelectedFiles();
|
||||
}
|
||||
|
||||
public void OnRenameButtonClicked()
|
||||
{
|
||||
Hide();
|
||||
fileBrowser.RenameSelectedFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d5261bc2717e6143961d30ccb76fb66
|
||||
timeCreated: 1603793977
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,61 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace SimpleFileBrowser
|
||||
{
|
||||
public class FileBrowserCursorHandler : MonoBehaviour
|
||||
#if UNITY_EDITOR || ( !UNITY_ANDROID && !UNITY_IOS )
|
||||
, IPointerEnterHandler, IPointerExitHandler, IBeginDragHandler, IEndDragHandler
|
||||
#endif
|
||||
{
|
||||
#if UNITY_EDITOR || ( !UNITY_ANDROID && !UNITY_IOS )
|
||||
#pragma warning disable 0649
|
||||
[SerializeField]
|
||||
private Texture2D resizeCursor;
|
||||
#pragma warning restore 0649
|
||||
|
||||
private bool isHovering;
|
||||
private bool isResizing;
|
||||
|
||||
void IPointerEnterHandler.OnPointerEnter( PointerEventData eventData )
|
||||
{
|
||||
isHovering = true;
|
||||
|
||||
if( !eventData.dragging )
|
||||
ShowResizeCursor();
|
||||
}
|
||||
|
||||
void IPointerExitHandler.OnPointerExit( PointerEventData eventData )
|
||||
{
|
||||
isHovering = false;
|
||||
|
||||
if( !isResizing )
|
||||
ShowDefaultCursor();
|
||||
}
|
||||
|
||||
void IBeginDragHandler.OnBeginDrag( PointerEventData eventData )
|
||||
{
|
||||
isResizing = true;
|
||||
ShowResizeCursor();
|
||||
}
|
||||
|
||||
void IEndDragHandler.OnEndDrag( PointerEventData eventData )
|
||||
{
|
||||
isResizing = false;
|
||||
|
||||
if( !isHovering )
|
||||
ShowDefaultCursor();
|
||||
}
|
||||
|
||||
private void ShowDefaultCursor()
|
||||
{
|
||||
Cursor.SetCursor( null, Vector2.zero, CursorMode.Auto );
|
||||
}
|
||||
|
||||
private void ShowResizeCursor()
|
||||
{
|
||||
Cursor.SetCursor( resizeCursor, new Vector2( resizeCursor.width * 0.5f, resizeCursor.height * 0.5f ), CursorMode.Auto );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 759524cf7ef37f244bb00cd9724f0349
|
||||
timeCreated: 1603745549
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,159 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
||||
using UnityEngine.InputSystem;
|
||||
#endif
|
||||
|
||||
namespace SimpleFileBrowser
|
||||
{
|
||||
public class FileBrowserFileOperationConfirmationPanel : MonoBehaviour
|
||||
{
|
||||
public enum OperationType { Delete = 0, Overwrite = 1 };
|
||||
|
||||
public delegate void OnOperationConfirmed();
|
||||
|
||||
#pragma warning disable 0649
|
||||
[SerializeField]
|
||||
private Text[] titleLabels;
|
||||
|
||||
[SerializeField]
|
||||
private GameObject[] targetItems;
|
||||
|
||||
[SerializeField]
|
||||
private Image[] targetItemIcons;
|
||||
|
||||
[SerializeField]
|
||||
private Text[] targetItemNames;
|
||||
|
||||
[SerializeField]
|
||||
private GameObject targetItemsRest;
|
||||
|
||||
[SerializeField]
|
||||
private Text targetItemsRestLabel;
|
||||
|
||||
[SerializeField]
|
||||
private RectTransform yesButtonTransform;
|
||||
|
||||
[SerializeField]
|
||||
private RectTransform noButtonTransform;
|
||||
|
||||
[SerializeField]
|
||||
private float narrowScreenWidth = 380f;
|
||||
#pragma warning restore 0649
|
||||
|
||||
private OnOperationConfirmed onOperationConfirmed;
|
||||
|
||||
internal void Show( FileBrowser fileBrowser, List<FileSystemEntry> items, OperationType operationType, OnOperationConfirmed onOperationConfirmed )
|
||||
{
|
||||
Show( fileBrowser, items, null, operationType, onOperationConfirmed );
|
||||
}
|
||||
|
||||
internal void Show( FileBrowser fileBrowser, List<FileSystemEntry> items, List<int> selectedItemIndices, OperationType operationType, OnOperationConfirmed onOperationConfirmed )
|
||||
{
|
||||
this.onOperationConfirmed = onOperationConfirmed;
|
||||
|
||||
int itemCount = ( selectedItemIndices != null ) ? selectedItemIndices.Count : items.Count;
|
||||
|
||||
for( int i = 0; i < titleLabels.Length; i++ )
|
||||
titleLabels[i].gameObject.SetActive( (int) operationType == i );
|
||||
|
||||
for( int i = 0; i < targetItems.Length; i++ )
|
||||
targetItems[i].SetActive( i < itemCount );
|
||||
|
||||
for( int i = 0; i < targetItems.Length && i < itemCount; i++ )
|
||||
{
|
||||
FileSystemEntry item = items[( selectedItemIndices != null ) ? selectedItemIndices[i] : i];
|
||||
targetItemIcons[i].sprite = fileBrowser.GetIconForFileEntry( item );
|
||||
targetItemNames[i].text = item.Name;
|
||||
}
|
||||
|
||||
if( itemCount > targetItems.Length )
|
||||
{
|
||||
targetItemsRestLabel.text = string.Concat( "...and ", ( itemCount - targetItems.Length ).ToString(), " other" );
|
||||
targetItemsRest.SetActive( true );
|
||||
}
|
||||
else
|
||||
targetItemsRest.SetActive( false );
|
||||
|
||||
gameObject.SetActive( true );
|
||||
}
|
||||
|
||||
// Handles responsive user interface
|
||||
internal void OnCanvasDimensionsChanged( Vector2 size )
|
||||
{
|
||||
if( size.x >= narrowScreenWidth )
|
||||
{
|
||||
yesButtonTransform.anchorMin = new Vector2( 0.5f, 0f );
|
||||
yesButtonTransform.anchorMax = new Vector2( 0.75f, 1f );
|
||||
noButtonTransform.anchorMin = new Vector2( 0.75f, 0f );
|
||||
}
|
||||
else
|
||||
{
|
||||
yesButtonTransform.anchorMin = Vector2.zero;
|
||||
yesButtonTransform.anchorMax = new Vector2( 0.5f, 1f );
|
||||
noButtonTransform.anchorMin = new Vector2( 0.5f, 0f );
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WSA || UNITY_WSA_10_0
|
||||
private void LateUpdate()
|
||||
{
|
||||
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
||||
if( Keyboard.current != null )
|
||||
#endif
|
||||
{
|
||||
// Handle keyboard shortcuts
|
||||
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
||||
if( Keyboard.current[Key.Enter].wasPressedThisFrame || Keyboard.current[Key.NumpadEnter].wasPressedThisFrame )
|
||||
#else
|
||||
if( Input.GetKeyDown( KeyCode.Return ) || Input.GetKeyDown( KeyCode.KeypadEnter ) )
|
||||
#endif
|
||||
YesButtonClicked();
|
||||
|
||||
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
||||
if( Keyboard.current[Key.Escape].wasPressedThisFrame )
|
||||
#else
|
||||
if( Input.GetKeyDown( KeyCode.Escape ) )
|
||||
#endif
|
||||
NoButtonClicked();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
internal void RefreshSkin( UISkin skin )
|
||||
{
|
||||
Image background = GetComponentInChildren<Image>();
|
||||
background.color = skin.PopupPanelsBackgroundColor;
|
||||
background.sprite = skin.PopupPanelsBackground;
|
||||
|
||||
skin.ApplyTo( yesButtonTransform.GetComponent<Button>() );
|
||||
skin.ApplyTo( noButtonTransform.GetComponent<Button>() );
|
||||
|
||||
for( int i = 0; i < titleLabels.Length; i++ )
|
||||
skin.ApplyTo( titleLabels[i], skin.PopupPanelsTextColor );
|
||||
|
||||
skin.ApplyTo( targetItemsRestLabel, skin.PopupPanelsTextColor );
|
||||
|
||||
for( int i = 0; i < targetItemNames.Length; i++ )
|
||||
skin.ApplyTo( targetItemNames[i], skin.PopupPanelsTextColor );
|
||||
|
||||
for( int i = 0; i < targetItems.Length; i++ )
|
||||
targetItems[i].GetComponent<LayoutElement>().preferredHeight = skin.FileHeight;
|
||||
}
|
||||
|
||||
public void YesButtonClicked()
|
||||
{
|
||||
gameObject.SetActive( false );
|
||||
|
||||
if( onOperationConfirmed != null )
|
||||
onOperationConfirmed();
|
||||
}
|
||||
|
||||
public void NoButtonClicked()
|
||||
{
|
||||
gameObject.SetActive( false );
|
||||
onOperationConfirmed = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 524a683efed82084b9a9c4a3eff23b73
|
||||
timeCreated: 1658958138
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
543
Assets/Plugins/SimpleFileBrowser/Scripts/FileBrowserHelpers.cs
Normal file
543
Assets/Plugins/SimpleFileBrowser/Scripts/FileBrowserHelpers.cs
Normal file
@@ -0,0 +1,543 @@
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SimpleFileBrowser
|
||||
{
|
||||
public struct FileSystemEntry
|
||||
{
|
||||
public readonly string Path;
|
||||
public readonly string Name;
|
||||
public readonly string Extension;
|
||||
public readonly FileAttributes Attributes;
|
||||
|
||||
public bool IsDirectory { get { return ( Attributes & FileAttributes.Directory ) == FileAttributes.Directory; } }
|
||||
|
||||
public FileSystemEntry( string path, string name, string extension, bool isDirectory )
|
||||
{
|
||||
Path = path;
|
||||
Name = name;
|
||||
Extension = extension;
|
||||
Attributes = isDirectory ? FileAttributes.Directory : FileAttributes.Normal;
|
||||
}
|
||||
|
||||
public FileSystemEntry( FileSystemInfo fileInfo, string extension )
|
||||
{
|
||||
Path = fileInfo.FullName;
|
||||
Name = fileInfo.Name;
|
||||
Extension = extension;
|
||||
Attributes = fileInfo.Attributes;
|
||||
}
|
||||
}
|
||||
|
||||
public static class FileBrowserHelpers
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
private static AndroidJavaClass m_ajc = null;
|
||||
public static AndroidJavaClass AJC
|
||||
{
|
||||
get
|
||||
{
|
||||
if( m_ajc == null )
|
||||
m_ajc = new AndroidJavaClass( "com.yasirkula.unity.FileBrowser" );
|
||||
|
||||
return m_ajc;
|
||||
}
|
||||
}
|
||||
|
||||
private static AndroidJavaObject m_context = null;
|
||||
public static AndroidJavaObject Context
|
||||
{
|
||||
get
|
||||
{
|
||||
if( m_context == null )
|
||||
{
|
||||
using( AndroidJavaObject unityClass = new AndroidJavaClass( "com.unity3d.player.UnityPlayer" ) )
|
||||
{
|
||||
m_context = unityClass.GetStatic<AndroidJavaObject>( "currentActivity" );
|
||||
}
|
||||
}
|
||||
|
||||
return m_context;
|
||||
}
|
||||
}
|
||||
|
||||
private static string m_temporaryFilePath = null;
|
||||
private static string TemporaryFilePath
|
||||
{
|
||||
get
|
||||
{
|
||||
if( m_temporaryFilePath == null )
|
||||
{
|
||||
m_temporaryFilePath = Path.Combine( Application.temporaryCachePath, "tmpFile" );
|
||||
Directory.CreateDirectory( Application.temporaryCachePath );
|
||||
}
|
||||
|
||||
return m_temporaryFilePath;
|
||||
}
|
||||
}
|
||||
|
||||
// On Android 10+, filesystem can be accessed via Storage Access Framework only
|
||||
private static bool? m_shouldUseSAF = null;
|
||||
public static bool ShouldUseSAF
|
||||
{
|
||||
get
|
||||
{
|
||||
if( m_shouldUseSAF == null )
|
||||
m_shouldUseSAF = AJC.CallStatic<bool>( "CheckSAF" );
|
||||
|
||||
return m_shouldUseSAF.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ShouldUseSAFForPath( string path ) // true: path should be managed with AJC (native helper class for Storage Access Framework), false: path should be managed with System.IO
|
||||
{
|
||||
return ShouldUseSAF && ( string.IsNullOrEmpty( path ) || path[0] != '/' );
|
||||
}
|
||||
#endif
|
||||
|
||||
public static bool FileExists( string path )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( path ) )
|
||||
return AJC.CallStatic<bool>( "SAFEntryExists", Context, path, false );
|
||||
#endif
|
||||
return File.Exists( path );
|
||||
}
|
||||
|
||||
public static bool DirectoryExists( string path )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( path ) )
|
||||
return AJC.CallStatic<bool>( "SAFEntryExists", Context, path, true );
|
||||
else if( ShouldUseSAF ) // Directory.Exists returns true even for inaccessible directories on Android 10+, we need to check if the directory is accessible
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.GetFiles( path, "testtesttest" );
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return Directory.Exists( path );
|
||||
}
|
||||
|
||||
public static bool IsDirectory( string path )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( path ) )
|
||||
return AJC.CallStatic<bool>( "SAFEntryDirectory", Context, path );
|
||||
#endif
|
||||
if( Directory.Exists( path ) )
|
||||
return true;
|
||||
if( File.Exists( path ) )
|
||||
return false;
|
||||
|
||||
string extension = Path.GetExtension( path );
|
||||
return extension == null || extension.Length <= 1; // extension includes '.'
|
||||
}
|
||||
|
||||
public static bool IsPathDescendantOfAnother( string path, string parentFolderPath )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( path ) )
|
||||
return AJC.CallStatic<bool>( "IsSAFEntryChildOfAnother", Context, path, parentFolderPath );
|
||||
#endif
|
||||
path = Path.GetFullPath( path ).Replace( '\\', '/' );
|
||||
parentFolderPath = Path.GetFullPath( parentFolderPath ).Replace( '\\', '/' );
|
||||
|
||||
if( path == parentFolderPath )
|
||||
return false;
|
||||
|
||||
if( parentFolderPath[parentFolderPath.Length - 1] != '/' )
|
||||
parentFolderPath += "/";
|
||||
|
||||
return path != parentFolderPath && path.StartsWith( parentFolderPath, System.StringComparison.OrdinalIgnoreCase );
|
||||
}
|
||||
|
||||
public static string GetDirectoryName( string path )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( path ) )
|
||||
return AJC.CallStatic<string>( "GetParentDirectory", Context, path );
|
||||
#endif
|
||||
return Path.GetDirectoryName( path );
|
||||
}
|
||||
|
||||
public static FileSystemEntry[] GetEntriesInDirectory( string path, bool extractOnlyLastSuffixFromExtensions )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( path ) )
|
||||
{
|
||||
string resultRaw = AJC.CallStatic<string>( "OpenSAFFolder", Context, path );
|
||||
int separatorIndex = resultRaw.IndexOf( "<>" );
|
||||
if( separatorIndex <= 0 )
|
||||
{
|
||||
Debug.LogError( "Entry count does not exist" );
|
||||
return null;
|
||||
}
|
||||
|
||||
int entryCount = 0;
|
||||
for( int i = 0; i < separatorIndex; i++ )
|
||||
{
|
||||
char ch = resultRaw[i];
|
||||
if( ch < '0' && ch > '9' )
|
||||
{
|
||||
Debug.LogError( "Couldn't parse entry count" );
|
||||
return null;
|
||||
}
|
||||
|
||||
entryCount = entryCount * 10 + ( ch - '0' );
|
||||
}
|
||||
|
||||
if( entryCount <= 0 )
|
||||
return null;
|
||||
|
||||
FileSystemEntry[] result = new FileSystemEntry[entryCount];
|
||||
for( int i = 0; i < entryCount; i++ )
|
||||
{
|
||||
separatorIndex += 2;
|
||||
if( separatorIndex >= resultRaw.Length )
|
||||
{
|
||||
Debug.LogError( "Couldn't fetch directory attribute" );
|
||||
return null;
|
||||
}
|
||||
|
||||
bool isDirectory = resultRaw[separatorIndex] == 'd';
|
||||
|
||||
separatorIndex++;
|
||||
int nextSeparatorIndex = resultRaw.IndexOf( "<>", separatorIndex );
|
||||
if( nextSeparatorIndex <= 0 )
|
||||
{
|
||||
Debug.LogError( "Entry name is empty" );
|
||||
return null;
|
||||
}
|
||||
|
||||
string entryName = resultRaw.Substring( separatorIndex, nextSeparatorIndex - separatorIndex );
|
||||
|
||||
separatorIndex = nextSeparatorIndex + 2;
|
||||
nextSeparatorIndex = resultRaw.IndexOf( "<>", separatorIndex );
|
||||
if( nextSeparatorIndex <= 0 )
|
||||
{
|
||||
Debug.LogError( "Entry rawUri is empty" );
|
||||
return null;
|
||||
}
|
||||
|
||||
string rawUri = resultRaw.Substring( separatorIndex, nextSeparatorIndex - separatorIndex );
|
||||
|
||||
separatorIndex = nextSeparatorIndex;
|
||||
|
||||
result[i] = new FileSystemEntry( rawUri, entryName, isDirectory ? null : FileBrowser.GetExtensionFromFilename( entryName, extractOnlyLastSuffixFromExtensions ), isDirectory );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
FileSystemInfo[] items = new DirectoryInfo( path ).GetFileSystemInfos();
|
||||
FileSystemEntry[] result = new FileSystemEntry[items.Length];
|
||||
int index = 0;
|
||||
for( int i = 0; i < items.Length; i++ )
|
||||
{
|
||||
try
|
||||
{
|
||||
result[index] = new FileSystemEntry( items[i], FileBrowser.GetExtensionFromFilename( items[i].Name, extractOnlyLastSuffixFromExtensions ) );
|
||||
index++;
|
||||
}
|
||||
catch( System.Exception e )
|
||||
{
|
||||
Debug.LogException( e );
|
||||
}
|
||||
}
|
||||
|
||||
if( result.Length != index )
|
||||
System.Array.Resize( ref result, index );
|
||||
|
||||
return result;
|
||||
}
|
||||
catch( System.UnauthorizedAccessException ) { }
|
||||
catch( System.Exception e )
|
||||
{
|
||||
Debug.LogException( e );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string CreateFileInDirectory( string directoryPath, string filename )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( directoryPath ) )
|
||||
return AJC.CallStatic<string>( "CreateSAFEntry", Context, directoryPath, false, filename );
|
||||
#endif
|
||||
|
||||
string path = Path.Combine( directoryPath, filename );
|
||||
using( File.Create( path ) ) { }
|
||||
return path;
|
||||
}
|
||||
|
||||
public static string CreateFolderInDirectory( string directoryPath, string folderName )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( directoryPath ) )
|
||||
return AJC.CallStatic<string>( "CreateSAFEntry", Context, directoryPath, true, folderName );
|
||||
#endif
|
||||
|
||||
string path = Path.Combine( directoryPath, folderName );
|
||||
Directory.CreateDirectory( path );
|
||||
return path;
|
||||
}
|
||||
|
||||
public static void WriteBytesToFile( string targetPath, byte[] bytes )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( targetPath ) )
|
||||
{
|
||||
File.WriteAllBytes( TemporaryFilePath, bytes );
|
||||
AJC.CallStatic( "WriteToSAFEntry", Context, targetPath, TemporaryFilePath, false );
|
||||
File.Delete( TemporaryFilePath );
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
File.WriteAllBytes( targetPath, bytes );
|
||||
}
|
||||
|
||||
public static void WriteTextToFile( string targetPath, string text )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( targetPath ) )
|
||||
{
|
||||
File.WriteAllText( TemporaryFilePath, text );
|
||||
AJC.CallStatic( "WriteToSAFEntry", Context, targetPath, TemporaryFilePath, false );
|
||||
File.Delete( TemporaryFilePath );
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
File.WriteAllText( targetPath, text );
|
||||
}
|
||||
|
||||
public static void AppendBytesToFile( string targetPath, byte[] bytes )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( targetPath ) )
|
||||
{
|
||||
File.WriteAllBytes( TemporaryFilePath, bytes );
|
||||
AJC.CallStatic( "WriteToSAFEntry", Context, targetPath, TemporaryFilePath, true );
|
||||
File.Delete( TemporaryFilePath );
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
using( var stream = new FileStream( targetPath, FileMode.Append, FileAccess.Write ) )
|
||||
{
|
||||
stream.Write( bytes, 0, bytes.Length );
|
||||
}
|
||||
}
|
||||
|
||||
public static void AppendTextToFile( string targetPath, string text )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( targetPath ) )
|
||||
{
|
||||
File.WriteAllText( TemporaryFilePath, text );
|
||||
AJC.CallStatic( "WriteToSAFEntry", Context, targetPath, TemporaryFilePath, true );
|
||||
File.Delete( TemporaryFilePath );
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
File.AppendAllText( targetPath, text );
|
||||
}
|
||||
|
||||
private static void AppendFileToFile( string targetPath, string sourceFileToAppend )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( targetPath ) )
|
||||
{
|
||||
AJC.CallStatic( "WriteToSAFEntry", Context, targetPath, sourceFileToAppend, true );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
using( Stream input = File.OpenRead( sourceFileToAppend ) )
|
||||
using( Stream output = new FileStream( targetPath, FileMode.Append, FileAccess.Write ) )
|
||||
{
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while( ( bytesRead = input.Read( buffer, 0, buffer.Length ) ) > 0 )
|
||||
output.Write( buffer, 0, bytesRead );
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] ReadBytesFromFile( string sourcePath )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( sourcePath ) )
|
||||
{
|
||||
AJC.CallStatic( "ReadFromSAFEntry", Context, sourcePath, TemporaryFilePath );
|
||||
byte[] result = File.ReadAllBytes( TemporaryFilePath );
|
||||
File.Delete( TemporaryFilePath );
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
return File.ReadAllBytes( sourcePath );
|
||||
}
|
||||
|
||||
public static string ReadTextFromFile( string sourcePath )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( sourcePath ) )
|
||||
{
|
||||
AJC.CallStatic( "ReadFromSAFEntry", Context, sourcePath, TemporaryFilePath );
|
||||
string result = File.ReadAllText( TemporaryFilePath );
|
||||
File.Delete( TemporaryFilePath );
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
return File.ReadAllText( sourcePath );
|
||||
}
|
||||
|
||||
public static void CopyFile( string sourcePath, string destinationPath )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAF ) // No need to use ShouldUseSAFForPath because both SAF paths and raw file paths are handled on the native-side
|
||||
{
|
||||
AJC.CallStatic( "CopyFile", Context, sourcePath, destinationPath, false );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
File.Copy( sourcePath, destinationPath, true );
|
||||
}
|
||||
|
||||
public static void CopyDirectory( string sourcePath, string destinationPath )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAF ) // No need to use ShouldUseSAFForPath because both SAF paths and raw directory paths are handled on the native-side
|
||||
{
|
||||
AJC.CallStatic( "CopyDirectory", Context, sourcePath, destinationPath, false );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
CopyDirectoryRecursively( new DirectoryInfo( sourcePath ), destinationPath );
|
||||
}
|
||||
|
||||
private static void CopyDirectoryRecursively( DirectoryInfo sourceDirectory, string destinationPath )
|
||||
{
|
||||
Directory.CreateDirectory( destinationPath );
|
||||
|
||||
FileInfo[] files = sourceDirectory.GetFiles();
|
||||
for( int i = 0; i < files.Length; i++ )
|
||||
files[i].CopyTo( Path.Combine( destinationPath, files[i].Name ), true );
|
||||
|
||||
DirectoryInfo[] subDirectories = sourceDirectory.GetDirectories();
|
||||
for( int i = 0; i < subDirectories.Length; i++ )
|
||||
CopyDirectoryRecursively( subDirectories[i], Path.Combine( destinationPath, subDirectories[i].Name ) );
|
||||
}
|
||||
|
||||
public static void MoveFile( string sourcePath, string destinationPath )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAF ) // No need to use ShouldUseSAFForPath because both SAF paths and raw file paths are handled on the native-side
|
||||
{
|
||||
AJC.CallStatic( "CopyFile", Context, sourcePath, destinationPath, true );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
File.Move( sourcePath, destinationPath );
|
||||
}
|
||||
|
||||
public static void MoveDirectory( string sourcePath, string destinationPath )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAF ) // No need to use ShouldUseSAFForPath because both SAF paths and raw directory paths are handled on the native-side
|
||||
{
|
||||
AJC.CallStatic( "CopyDirectory", Context, sourcePath, destinationPath, true );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
Directory.Move( sourcePath, destinationPath );
|
||||
}
|
||||
|
||||
public static string RenameFile( string path, string newName )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( path ) )
|
||||
return AJC.CallStatic<string>( "RenameSAFEntry", Context, path, newName );
|
||||
#endif
|
||||
string newPath = Path.Combine( Path.GetDirectoryName( path ), newName );
|
||||
File.Move( path, newPath );
|
||||
|
||||
return newPath;
|
||||
}
|
||||
|
||||
public static string RenameDirectory( string path, string newName )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( path ) )
|
||||
return AJC.CallStatic<string>( "RenameSAFEntry", Context, path, newName );
|
||||
#endif
|
||||
string newPath = Path.Combine( new DirectoryInfo( path ).Parent.FullName, newName );
|
||||
Directory.Move( path, newPath );
|
||||
|
||||
return newPath;
|
||||
}
|
||||
|
||||
public static void DeleteFile( string path )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( path ) )
|
||||
{
|
||||
AJC.CallStatic<bool>( "DeleteSAFEntry", Context, path );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
File.Delete( path );
|
||||
}
|
||||
|
||||
public static void DeleteDirectory( string path )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( path ) )
|
||||
{
|
||||
AJC.CallStatic<bool>( "DeleteSAFEntry", Context, path );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
Directory.Delete( path, true );
|
||||
}
|
||||
|
||||
public static string GetFilename( string path )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( path ) )
|
||||
return AJC.CallStatic<string>( "SAFEntryName", Context, path );
|
||||
#endif
|
||||
return Path.GetFileName( path );
|
||||
}
|
||||
|
||||
public static long GetFilesize( string path )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
if( ShouldUseSAFForPath( path ) )
|
||||
return AJC.CallStatic<long>( "SAFEntrySize", Context, path );
|
||||
#endif
|
||||
return new FileInfo( path ).Length;
|
||||
}
|
||||
|
||||
public static System.DateTime GetLastModifiedDate( string path )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
// Credit: https://stackoverflow.com/a/28504416/2373034
|
||||
if( ShouldUseSAFForPath( path ) )
|
||||
return new System.DateTime( 1970, 1, 1, 0, 0, 0 ).AddMilliseconds( AJC.CallStatic<long>( "SAFEntryLastModified", Context, path ) );
|
||||
#endif
|
||||
return new FileInfo( path ).LastWriteTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2370e7a82ec4087499ebf7efa149e9eb
|
||||
timeCreated: 1570919647
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
238
Assets/Plugins/SimpleFileBrowser/Scripts/FileBrowserItem.cs
Normal file
238
Assets/Plugins/SimpleFileBrowser/Scripts/FileBrowserItem.cs
Normal file
@@ -0,0 +1,238 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace SimpleFileBrowser
|
||||
{
|
||||
public class FileBrowserItem : ListItem, IPointerClickHandler, IPointerDownHandler, IPointerUpHandler
|
||||
#if UNITY_EDITOR || ( !UNITY_ANDROID && !UNITY_IOS )
|
||||
, IPointerEnterHandler, IPointerExitHandler
|
||||
#endif
|
||||
{
|
||||
#region Constants
|
||||
private const float DOUBLE_CLICK_TIME = 0.5f;
|
||||
private const float TOGGLE_MULTI_SELECTION_HOLD_TIME = 0.5f;
|
||||
#endregion
|
||||
|
||||
#region Variables
|
||||
protected FileBrowser fileBrowser;
|
||||
|
||||
#pragma warning disable 0649
|
||||
[SerializeField]
|
||||
private Image background;
|
||||
|
||||
[SerializeField]
|
||||
private Image icon;
|
||||
public Image Icon { get { return icon; } }
|
||||
|
||||
[SerializeField]
|
||||
private Image multiSelectionToggle;
|
||||
|
||||
[SerializeField]
|
||||
private Text nameText;
|
||||
#pragma warning restore 0649
|
||||
|
||||
#pragma warning disable 0414
|
||||
private bool isSelected, isHidden;
|
||||
#pragma warning restore 0414
|
||||
|
||||
private UISkin skin;
|
||||
|
||||
private float pressTime = Mathf.Infinity;
|
||||
private float prevClickTime;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
private RectTransform m_transform;
|
||||
public RectTransform TransformComponent
|
||||
{
|
||||
get
|
||||
{
|
||||
if( m_transform == null )
|
||||
m_transform = (RectTransform) transform;
|
||||
|
||||
return m_transform;
|
||||
}
|
||||
}
|
||||
|
||||
public string Name { get { return nameText.text; } }
|
||||
public bool IsDirectory { get; private set; }
|
||||
#endregion
|
||||
|
||||
#region Initialization Functions
|
||||
public void SetFileBrowser( FileBrowser fileBrowser, UISkin skin )
|
||||
{
|
||||
this.fileBrowser = fileBrowser;
|
||||
OnSkinRefreshed( skin, false );
|
||||
}
|
||||
|
||||
public void SetFile( Sprite icon, string name, bool isDirectory )
|
||||
{
|
||||
this.icon.sprite = icon;
|
||||
nameText.text = name;
|
||||
|
||||
IsDirectory = isDirectory;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Messages
|
||||
private void Update()
|
||||
{
|
||||
if( fileBrowser.AllowMultiSelection && Time.realtimeSinceStartup - pressTime >= TOGGLE_MULTI_SELECTION_HOLD_TIME )
|
||||
{
|
||||
// Item is held for a while
|
||||
pressTime = Mathf.Infinity;
|
||||
fileBrowser.OnItemHeld( this );
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Pointer Events
|
||||
public void OnPointerClick( PointerEventData eventData )
|
||||
{
|
||||
#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WEBGL || UNITY_WSA || UNITY_WSA_10_0
|
||||
if( eventData.button == PointerEventData.InputButton.Middle )
|
||||
return;
|
||||
else if( eventData.button == PointerEventData.InputButton.Right )
|
||||
{
|
||||
// First, select the item
|
||||
if( !isSelected )
|
||||
{
|
||||
prevClickTime = 0f;
|
||||
fileBrowser.OnItemSelected( this, false );
|
||||
}
|
||||
|
||||
// Then, show the context menu
|
||||
fileBrowser.OnContextMenuTriggered( eventData.position );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if( Time.realtimeSinceStartup - prevClickTime < DOUBLE_CLICK_TIME )
|
||||
{
|
||||
prevClickTime = 0f;
|
||||
fileBrowser.OnItemSelected( this, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
prevClickTime = Time.realtimeSinceStartup;
|
||||
fileBrowser.OnItemSelected( this, false );
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPointerDown( PointerEventData eventData )
|
||||
{
|
||||
#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WEBGL || UNITY_WSA || UNITY_WSA_10_0
|
||||
if( eventData.button != PointerEventData.InputButton.Left )
|
||||
return;
|
||||
#endif
|
||||
|
||||
pressTime = Time.realtimeSinceStartup;
|
||||
}
|
||||
|
||||
public void OnPointerUp( PointerEventData eventData )
|
||||
{
|
||||
#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WEBGL || UNITY_WSA || UNITY_WSA_10_0
|
||||
if( eventData.button != PointerEventData.InputButton.Left )
|
||||
return;
|
||||
#endif
|
||||
|
||||
if( pressTime != Mathf.Infinity )
|
||||
pressTime = Mathf.Infinity;
|
||||
else if( fileBrowser.MultiSelectionToggleSelectionMode )
|
||||
{
|
||||
// We have activated MultiSelectionToggleSelectionMode with this press, processing the click would result in
|
||||
// deselecting this item since its selected state would be toggled
|
||||
eventData.eligibleForClick = false;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR || ( !UNITY_ANDROID && !UNITY_IOS )
|
||||
public void OnPointerEnter( PointerEventData eventData )
|
||||
{
|
||||
if( !isSelected )
|
||||
background.color = skin.FileHoveredBackgroundColor;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR || ( !UNITY_ANDROID && !UNITY_IOS )
|
||||
public void OnPointerExit( PointerEventData eventData )
|
||||
{
|
||||
if( !isSelected )
|
||||
background.color = ( Position % 2 ) == 0 ? skin.FileNormalBackgroundColor : skin.FileAlternatingBackgroundColor;
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region Other Events
|
||||
public void SetSelected( bool isSelected )
|
||||
{
|
||||
this.isSelected = isSelected;
|
||||
|
||||
background.color = isSelected ? skin.FileSelectedBackgroundColor : ( ( Position % 2 ) == 0 ? skin.FileNormalBackgroundColor : skin.FileAlternatingBackgroundColor );
|
||||
nameText.color = isSelected ? skin.FileSelectedTextColor : skin.FileNormalTextColor;
|
||||
|
||||
if( isHidden )
|
||||
{
|
||||
Color c = nameText.color;
|
||||
c.a = 0.55f;
|
||||
nameText.color = c;
|
||||
}
|
||||
|
||||
if( multiSelectionToggle ) // Quick links don't have multi-selection toggle
|
||||
{
|
||||
// Don't show multi-selection toggle for folders in file selection mode
|
||||
if( fileBrowser.MultiSelectionToggleSelectionMode && ( !IsDirectory || fileBrowser.PickerMode != FileBrowser.PickMode.Files ) )
|
||||
{
|
||||
if( !multiSelectionToggle.gameObject.activeSelf )
|
||||
{
|
||||
multiSelectionToggle.gameObject.SetActive( true );
|
||||
|
||||
Vector2 shiftAmount = new Vector2( multiSelectionToggle.rectTransform.sizeDelta.x, 0f );
|
||||
icon.rectTransform.anchoredPosition += shiftAmount;
|
||||
nameText.rectTransform.anchoredPosition += shiftAmount;
|
||||
}
|
||||
|
||||
multiSelectionToggle.sprite = isSelected ? skin.FileMultiSelectionToggleOnIcon : skin.FileMultiSelectionToggleOffIcon;
|
||||
}
|
||||
else if( multiSelectionToggle.gameObject.activeSelf )
|
||||
{
|
||||
multiSelectionToggle.gameObject.SetActive( false );
|
||||
|
||||
Vector2 shiftAmount = new Vector2( -multiSelectionToggle.rectTransform.sizeDelta.x, 0f );
|
||||
icon.rectTransform.anchoredPosition += shiftAmount;
|
||||
nameText.rectTransform.anchoredPosition += shiftAmount;
|
||||
|
||||
// Clicking a file shortly after disabling MultiSelectionToggleSelectionMode does nothing, this workaround fixes that issue
|
||||
prevClickTime = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetHidden( bool isHidden )
|
||||
{
|
||||
this.isHidden = isHidden;
|
||||
|
||||
Color c = icon.color;
|
||||
c.a = isHidden ? 0.5f : 1f;
|
||||
icon.color = c;
|
||||
|
||||
c = nameText.color;
|
||||
c.a = isHidden ? 0.55f : ( isSelected ? skin.FileSelectedTextColor.a : skin.FileNormalTextColor.a );
|
||||
nameText.color = c;
|
||||
}
|
||||
|
||||
public void OnSkinRefreshed( UISkin skin, bool isInitialized = true )
|
||||
{
|
||||
this.skin = skin;
|
||||
|
||||
TransformComponent.sizeDelta = new Vector2( TransformComponent.sizeDelta.x, skin.FileHeight );
|
||||
skin.ApplyTo( nameText, isSelected ? skin.FileSelectedTextColor : skin.FileNormalTextColor );
|
||||
icon.rectTransform.sizeDelta = new Vector2( icon.rectTransform.sizeDelta.x, -skin.FileIconsPadding );
|
||||
|
||||
if( isInitialized )
|
||||
SetSelected( isSelected );
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5f1b2825c50f7b4d9be146ab2137bff
|
||||
timeCreated: 1479417850
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
105
Assets/Plugins/SimpleFileBrowser/Scripts/FileBrowserMovement.cs
Normal file
105
Assets/Plugins/SimpleFileBrowser/Scripts/FileBrowserMovement.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace SimpleFileBrowser
|
||||
{
|
||||
public class FileBrowserMovement : MonoBehaviour
|
||||
{
|
||||
#region Variables
|
||||
#pragma warning disable 0649
|
||||
private FileBrowser fileBrowser;
|
||||
private RectTransform canvasTR;
|
||||
private Camera canvasCam;
|
||||
|
||||
[SerializeField]
|
||||
private RectTransform window;
|
||||
|
||||
[SerializeField]
|
||||
private RecycledListView listView;
|
||||
#pragma warning restore 0649
|
||||
|
||||
private Vector2 initialTouchPos = Vector2.zero;
|
||||
private Vector2 initialAnchoredPos, initialSizeDelta;
|
||||
#endregion
|
||||
|
||||
#region Initialization Functions
|
||||
public void Initialize( FileBrowser fileBrowser )
|
||||
{
|
||||
this.fileBrowser = fileBrowser;
|
||||
canvasTR = fileBrowser.GetComponent<RectTransform>();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Pointer Events
|
||||
public void OnDragStarted( BaseEventData data )
|
||||
{
|
||||
PointerEventData pointer = (PointerEventData) data;
|
||||
|
||||
canvasCam = pointer.pressEventCamera;
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle( window, pointer.pressPosition, canvasCam, out initialTouchPos );
|
||||
}
|
||||
|
||||
public void OnDrag( BaseEventData data )
|
||||
{
|
||||
PointerEventData pointer = (PointerEventData) data;
|
||||
|
||||
Vector2 touchPos;
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle( window, pointer.position, canvasCam, out touchPos );
|
||||
window.anchoredPosition += touchPos - initialTouchPos;
|
||||
}
|
||||
|
||||
public void OnEndDrag( BaseEventData data )
|
||||
{
|
||||
fileBrowser.EnsureWindowIsWithinBounds();
|
||||
}
|
||||
|
||||
public void OnResizeStarted( BaseEventData data )
|
||||
{
|
||||
PointerEventData pointer = (PointerEventData) data;
|
||||
|
||||
canvasCam = pointer.pressEventCamera;
|
||||
initialAnchoredPos = window.anchoredPosition;
|
||||
initialSizeDelta = window.sizeDelta;
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle( canvasTR, pointer.pressPosition, canvasCam, out initialTouchPos );
|
||||
}
|
||||
|
||||
public void OnResize( BaseEventData data )
|
||||
{
|
||||
PointerEventData pointer = (PointerEventData) data;
|
||||
|
||||
Vector2 touchPos;
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle( canvasTR, pointer.position, canvasCam, out touchPos );
|
||||
|
||||
Vector2 delta = touchPos - initialTouchPos;
|
||||
Vector2 newSize = initialSizeDelta + new Vector2( delta.x, -delta.y );
|
||||
Vector2 canvasSize = canvasTR.sizeDelta;
|
||||
|
||||
if( newSize.x < fileBrowser.minWidth ) newSize.x = fileBrowser.minWidth;
|
||||
if( newSize.y < fileBrowser.minHeight ) newSize.y = fileBrowser.minHeight;
|
||||
|
||||
if( newSize.x > canvasSize.x ) newSize.x = canvasSize.x;
|
||||
if( newSize.y > canvasSize.y ) newSize.y = canvasSize.y;
|
||||
|
||||
newSize.x = (int) newSize.x;
|
||||
newSize.y = (int) newSize.y;
|
||||
|
||||
delta = newSize - initialSizeDelta;
|
||||
|
||||
window.anchoredPosition = initialAnchoredPos + new Vector2( delta.x * 0.5f, delta.y * -0.5f );
|
||||
|
||||
if( window.sizeDelta != newSize )
|
||||
{
|
||||
window.sizeDelta = newSize;
|
||||
fileBrowser.OnWindowDimensionsChanged( newSize );
|
||||
}
|
||||
|
||||
listView.OnViewportDimensionsChanged();
|
||||
}
|
||||
|
||||
public void OnEndResize( BaseEventData data )
|
||||
{
|
||||
fileBrowser.EnsureWindowIsWithinBounds();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 46d41d79fe7c3d44ca846b4f3d81a476
|
||||
timeCreated: 1479486534
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,21 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace SimpleFileBrowser
|
||||
{
|
||||
public class FileBrowserQuickLink : FileBrowserItem
|
||||
{
|
||||
#region Properties
|
||||
private string m_targetPath;
|
||||
public string TargetPath { get { return m_targetPath; } }
|
||||
#endregion
|
||||
|
||||
#region Initialization Functions
|
||||
public void SetQuickLink( Sprite icon, string name, string targetPath )
|
||||
{
|
||||
SetFile( icon, name, true );
|
||||
|
||||
m_targetPath = targetPath;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f277f5418eabf94cad94208055878af
|
||||
timeCreated: 1479417850
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,79 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
||||
using UnityEngine.InputSystem;
|
||||
#endif
|
||||
|
||||
namespace SimpleFileBrowser
|
||||
{
|
||||
public class FileBrowserRenamedItem : MonoBehaviour
|
||||
{
|
||||
public delegate void OnRenameCompleted( string filename );
|
||||
|
||||
#pragma warning disable 0649
|
||||
[SerializeField]
|
||||
private Image background;
|
||||
|
||||
[SerializeField]
|
||||
private Image icon;
|
||||
|
||||
[SerializeField]
|
||||
private InputField nameInputField;
|
||||
public InputField InputField { get { return nameInputField; } }
|
||||
#pragma warning restore 0649
|
||||
|
||||
private OnRenameCompleted onRenameCompleted;
|
||||
|
||||
private RectTransform m_transform;
|
||||
public RectTransform TransformComponent
|
||||
{
|
||||
get
|
||||
{
|
||||
if( m_transform == null )
|
||||
m_transform = (RectTransform) transform;
|
||||
|
||||
return m_transform;
|
||||
}
|
||||
}
|
||||
|
||||
public void Show( string initialFilename, Color backgroundColor, Sprite icon, OnRenameCompleted onRenameCompleted )
|
||||
{
|
||||
background.color = backgroundColor;
|
||||
this.icon.sprite = icon;
|
||||
this.onRenameCompleted = onRenameCompleted;
|
||||
|
||||
transform.SetAsLastSibling();
|
||||
gameObject.SetActive( true );
|
||||
|
||||
nameInputField.text = initialFilename;
|
||||
nameInputField.ActivateInputField();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WSA || UNITY_WSA_10_0
|
||||
private void LateUpdate()
|
||||
{
|
||||
// Don't allow scrolling with mouse wheel while renaming a file or creating a folder
|
||||
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
||||
if( Mouse.current != null && Mouse.current.scroll.ReadValue().y != 0f )
|
||||
#else
|
||||
if( Input.mouseScrollDelta.y != 0f )
|
||||
#endif
|
||||
nameInputField.DeactivateInputField();
|
||||
}
|
||||
#endif
|
||||
|
||||
public void OnInputFieldEndEdit( string filename )
|
||||
{
|
||||
gameObject.SetActive( false );
|
||||
|
||||
// If we don't deselect the InputField manually, FileBrowser's keyboard shortcuts
|
||||
// no longer work until user clicks on a UI element and thus, deselects the InputField
|
||||
if( EventSystem.current && !EventSystem.current.alreadySelecting && EventSystem.current.currentSelectedGameObject == nameInputField.gameObject )
|
||||
EventSystem.current.SetSelectedGameObject( null );
|
||||
|
||||
if( onRenameCompleted != null )
|
||||
onRenameCompleted( filename );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7397ff7ae1ba4c47b6dfd3c84936584
|
||||
timeCreated: 1603812277
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,19 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace SimpleFileBrowser
|
||||
{
|
||||
// Credit: http://answers.unity.com/answers/1157876/view.html
|
||||
[RequireComponent( typeof( CanvasRenderer ) )]
|
||||
public class NonDrawingGraphic : Graphic
|
||||
{
|
||||
public override void SetMaterialDirty() { return; }
|
||||
public override void SetVerticesDirty() { return; }
|
||||
|
||||
protected override void OnPopulateMesh( VertexHelper vh )
|
||||
{
|
||||
vh.Clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b4fd8cdb8c068dd4bb48c415877496ba
|
||||
timeCreated: 1603794018
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dec11495f4b8cef49b7a3b4b06f094c3
|
||||
folderAsset: yes
|
||||
timeCreated: 1485706514
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace SimpleFileBrowser
|
||||
{
|
||||
public delegate void OnItemClickedHandler( ListItem item );
|
||||
|
||||
public interface IListViewAdapter
|
||||
{
|
||||
OnItemClickedHandler OnItemClicked { get; set; }
|
||||
|
||||
int Count { get; }
|
||||
float ItemHeight { get; }
|
||||
|
||||
ListItem CreateItem();
|
||||
|
||||
void SetItemContent( ListItem item );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08e51b912648ace4784ebe20fc6cc961
|
||||
timeCreated: 1485706575
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace SimpleFileBrowser
|
||||
{
|
||||
[RequireComponent( typeof( RectTransform ) )]
|
||||
public class ListItem : MonoBehaviour
|
||||
{
|
||||
public object Tag { get; set; }
|
||||
public int Position { get; set; }
|
||||
|
||||
private IListViewAdapter adapter;
|
||||
|
||||
internal void SetAdapter( IListViewAdapter listView )
|
||||
{
|
||||
this.adapter = listView;
|
||||
}
|
||||
|
||||
public void OnClick()
|
||||
{
|
||||
if( adapter.OnItemClicked != null )
|
||||
adapter.OnItemClicked( this );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c3e7249b2cb96446a7ccfbed51aab81
|
||||
timeCreated: 1485706535
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,252 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace SimpleFileBrowser
|
||||
{
|
||||
[RequireComponent( typeof( ScrollRect ) )]
|
||||
public class RecycledListView : MonoBehaviour
|
||||
#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WSA || UNITY_WSA_10_0
|
||||
, IPointerClickHandler
|
||||
#endif
|
||||
{
|
||||
#pragma warning disable 0649
|
||||
#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WSA || UNITY_WSA_10_0
|
||||
[SerializeField]
|
||||
private FileBrowser fileBrowser;
|
||||
#endif
|
||||
|
||||
// Cached components
|
||||
[SerializeField]
|
||||
private RectTransform viewportTransform;
|
||||
[SerializeField]
|
||||
private RectTransform contentTransform;
|
||||
#pragma warning restore 0649
|
||||
|
||||
private float itemHeight, _1OverItemHeight;
|
||||
private float viewportHeight;
|
||||
|
||||
private readonly Dictionary<int, ListItem> items = new Dictionary<int, ListItem>();
|
||||
private readonly Stack<ListItem> pooledItems = new Stack<ListItem>();
|
||||
|
||||
IListViewAdapter adapter = null;
|
||||
|
||||
// Current indices of items shown on screen
|
||||
private int currentTopIndex = -1, currentBottomIndex = -1;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
viewportHeight = viewportTransform.rect.height;
|
||||
GetComponent<ScrollRect>().onValueChanged.AddListener( ( pos ) => UpdateItemsInTheList() );
|
||||
}
|
||||
|
||||
public void SetAdapter( IListViewAdapter adapter )
|
||||
{
|
||||
this.adapter = adapter;
|
||||
|
||||
itemHeight = adapter.ItemHeight;
|
||||
_1OverItemHeight = 1f / itemHeight;
|
||||
}
|
||||
|
||||
public void OnSkinRefreshed()
|
||||
{
|
||||
if( currentTopIndex >= 0 )
|
||||
{
|
||||
DestroyItemsBetweenIndices( currentTopIndex, currentBottomIndex );
|
||||
currentTopIndex = currentBottomIndex = -1;
|
||||
}
|
||||
|
||||
itemHeight = adapter.ItemHeight;
|
||||
_1OverItemHeight = 1f / itemHeight;
|
||||
|
||||
UpdateList();
|
||||
}
|
||||
|
||||
// Update the list
|
||||
public void UpdateList()
|
||||
{
|
||||
float newHeight = Mathf.Max( 1f, adapter.Count * itemHeight );
|
||||
contentTransform.sizeDelta = new Vector2( 0f, newHeight );
|
||||
viewportHeight = viewportTransform.rect.height;
|
||||
|
||||
UpdateItemsInTheList( true );
|
||||
}
|
||||
|
||||
// Window is resized, update the list
|
||||
public void OnViewportDimensionsChanged()
|
||||
{
|
||||
viewportHeight = viewportTransform.rect.height;
|
||||
UpdateItemsInTheList();
|
||||
}
|
||||
|
||||
// Calculate the indices of items to show
|
||||
private void UpdateItemsInTheList( bool updateAllVisibleItems = false )
|
||||
{
|
||||
// If there is at least one item to show
|
||||
if( adapter.Count > 0 )
|
||||
{
|
||||
float contentPos = contentTransform.anchoredPosition.y - 1f;
|
||||
|
||||
int newTopIndex = (int) ( contentPos * _1OverItemHeight );
|
||||
int newBottomIndex = (int) ( ( contentPos + viewportHeight + 2f ) * _1OverItemHeight );
|
||||
|
||||
if( newTopIndex < 0 )
|
||||
newTopIndex = 0;
|
||||
|
||||
if( newBottomIndex > adapter.Count - 1 )
|
||||
newBottomIndex = adapter.Count - 1;
|
||||
|
||||
if( currentTopIndex == -1 )
|
||||
{
|
||||
// There are no active items
|
||||
|
||||
updateAllVisibleItems = true;
|
||||
|
||||
currentTopIndex = newTopIndex;
|
||||
currentBottomIndex = newBottomIndex;
|
||||
|
||||
CreateItemsBetweenIndices( newTopIndex, newBottomIndex );
|
||||
}
|
||||
else
|
||||
{
|
||||
// There are some active items
|
||||
|
||||
if( newBottomIndex < currentTopIndex || newTopIndex > currentBottomIndex )
|
||||
{
|
||||
// If user scrolled a lot such that, none of the items are now within
|
||||
// the bounds of the scroll view, pool all the previous items and create
|
||||
// new items for the new list of visible entries
|
||||
updateAllVisibleItems = true;
|
||||
|
||||
DestroyItemsBetweenIndices( currentTopIndex, currentBottomIndex );
|
||||
CreateItemsBetweenIndices( newTopIndex, newBottomIndex );
|
||||
}
|
||||
else
|
||||
{
|
||||
// User did not scroll a lot such that, some items are are still within
|
||||
// the bounds of the scroll view. Don't destroy them but update their content,
|
||||
// if necessary
|
||||
if( newTopIndex > currentTopIndex )
|
||||
{
|
||||
DestroyItemsBetweenIndices( currentTopIndex, newTopIndex - 1 );
|
||||
}
|
||||
|
||||
if( newBottomIndex < currentBottomIndex )
|
||||
{
|
||||
DestroyItemsBetweenIndices( newBottomIndex + 1, currentBottomIndex );
|
||||
}
|
||||
|
||||
if( newTopIndex < currentTopIndex )
|
||||
{
|
||||
CreateItemsBetweenIndices( newTopIndex, currentTopIndex - 1 );
|
||||
|
||||
// If it is not necessary to update all the items,
|
||||
// then just update the newly created items. Otherwise,
|
||||
// wait for the major update
|
||||
if( !updateAllVisibleItems )
|
||||
{
|
||||
UpdateItemContentsBetweenIndices( newTopIndex, currentTopIndex - 1 );
|
||||
}
|
||||
}
|
||||
|
||||
if( newBottomIndex > currentBottomIndex )
|
||||
{
|
||||
CreateItemsBetweenIndices( currentBottomIndex + 1, newBottomIndex );
|
||||
|
||||
// If it is not necessary to update all the items,
|
||||
// then just update the newly created items. Otherwise,
|
||||
// wait for the major update
|
||||
if( !updateAllVisibleItems )
|
||||
{
|
||||
UpdateItemContentsBetweenIndices( currentBottomIndex + 1, newBottomIndex );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentTopIndex = newTopIndex;
|
||||
currentBottomIndex = newBottomIndex;
|
||||
}
|
||||
|
||||
if( updateAllVisibleItems )
|
||||
{
|
||||
// Update all the items
|
||||
UpdateItemContentsBetweenIndices( currentTopIndex, currentBottomIndex );
|
||||
}
|
||||
}
|
||||
else if( currentTopIndex != -1 )
|
||||
{
|
||||
// There is nothing to show but some items are still visible; pool them
|
||||
DestroyItemsBetweenIndices( currentTopIndex, currentBottomIndex );
|
||||
|
||||
currentTopIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateItemsBetweenIndices( int topIndex, int bottomIndex )
|
||||
{
|
||||
for( int i = topIndex; i <= bottomIndex; i++ )
|
||||
{
|
||||
CreateItemAtIndex( i );
|
||||
}
|
||||
}
|
||||
|
||||
// Create (or unpool) an item
|
||||
private void CreateItemAtIndex( int index )
|
||||
{
|
||||
ListItem item;
|
||||
if( pooledItems.Count > 0 )
|
||||
{
|
||||
item = pooledItems.Pop();
|
||||
item.gameObject.SetActive( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
item = adapter.CreateItem();
|
||||
item.transform.SetParent( contentTransform, false );
|
||||
item.SetAdapter( adapter );
|
||||
}
|
||||
|
||||
// Reposition the item
|
||||
( (RectTransform) item.transform ).anchoredPosition = new Vector2( 1f, -index * itemHeight );
|
||||
|
||||
// To access this item easily in the future, add it to the dictionary
|
||||
items[index] = item;
|
||||
}
|
||||
|
||||
private void DestroyItemsBetweenIndices( int topIndex, int bottomIndex )
|
||||
{
|
||||
for( int i = topIndex; i <= bottomIndex; i++ )
|
||||
{
|
||||
ListItem item = items[i];
|
||||
|
||||
item.gameObject.SetActive( false );
|
||||
pooledItems.Push( item );
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateItemContentsBetweenIndices( int topIndex, int bottomIndex )
|
||||
{
|
||||
for( int i = topIndex; i <= bottomIndex; i++ )
|
||||
{
|
||||
ListItem item = items[i];
|
||||
|
||||
item.Position = i;
|
||||
adapter.SetItemContent( item );
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WSA || UNITY_WSA_10_0
|
||||
// When free space inside ScrollRect is clicked:
|
||||
// Left click: deselect selected file(s)
|
||||
// Right click: show context menu
|
||||
public void OnPointerClick( PointerEventData eventData )
|
||||
{
|
||||
if( eventData.button == PointerEventData.InputButton.Left )
|
||||
fileBrowser.DeselectAllFiles();
|
||||
else if( eventData.button == PointerEventData.InputButton.Right )
|
||||
fileBrowser.OnContextMenuTriggered( eventData.position );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 87ad67b4806678e40a492e337338760b
|
||||
timeCreated: 1485620000
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
651
Assets/Plugins/SimpleFileBrowser/Scripts/UISkin.cs
Normal file
651
Assets/Plugins/SimpleFileBrowser/Scripts/UISkin.cs
Normal file
@@ -0,0 +1,651 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace SimpleFileBrowser
|
||||
{
|
||||
[Serializable]
|
||||
public struct FiletypeIcon
|
||||
{
|
||||
public string extension;
|
||||
public Sprite icon;
|
||||
}
|
||||
|
||||
[CreateAssetMenu( fileName = "UI Skin", menuName = "yasirkula/SimpleFileBrowser/UI Skin", order = 111 )]
|
||||
public class UISkin : ScriptableObject
|
||||
{
|
||||
private int m_version = 0;
|
||||
public int Version { get { return m_version; } }
|
||||
|
||||
[ContextMenu( "Refresh UI" )]
|
||||
private void Invalidate()
|
||||
{
|
||||
m_version = UnityEngine.Random.Range( int.MinValue / 2, int.MaxValue / 2 );
|
||||
initializedFiletypeIcons = false;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected virtual void OnValidate()
|
||||
{
|
||||
// Refresh all UIs that use this skin
|
||||
Invalidate();
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma warning disable 0649
|
||||
[Header( "General" )]
|
||||
[SerializeField]
|
||||
private Font m_font;
|
||||
public Font Font
|
||||
{
|
||||
get { return m_font; }
|
||||
set { if( m_font != value ) { m_font = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private int m_fontSize = 14;
|
||||
public int FontSize
|
||||
{
|
||||
get { return m_fontSize; }
|
||||
set { if( m_fontSize != value ) { m_fontSize = value; m_version++; } }
|
||||
}
|
||||
|
||||
[Header( "File Browser Window" )]
|
||||
[SerializeField]
|
||||
private Color m_windowColor = Color.grey;
|
||||
public Color WindowColor
|
||||
{
|
||||
get { return m_windowColor; }
|
||||
set { if( m_windowColor != value ) { m_windowColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_filesListColor = Color.white;
|
||||
public Color FilesListColor
|
||||
{
|
||||
get { return m_filesListColor; }
|
||||
set { if( m_filesListColor != value ) { m_filesListColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_filesVerticalSeparatorColor = Color.grey;
|
||||
public Color FilesVerticalSeparatorColor
|
||||
{
|
||||
get { return m_filesVerticalSeparatorColor; }
|
||||
set { if( m_filesVerticalSeparatorColor != value ) { m_filesVerticalSeparatorColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_titleBackgroundColor = Color.black;
|
||||
public Color TitleBackgroundColor
|
||||
{
|
||||
get { return m_titleBackgroundColor; }
|
||||
set { if( m_titleBackgroundColor != value ) { m_titleBackgroundColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_titleTextColor = Color.white;
|
||||
public Color TitleTextColor
|
||||
{
|
||||
get { return m_titleTextColor; }
|
||||
set { if( m_titleTextColor != value ) { m_titleTextColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_windowResizeGizmoColor = Color.black;
|
||||
public Color WindowResizeGizmoColor
|
||||
{
|
||||
get { return m_windowResizeGizmoColor; }
|
||||
set { if( m_windowResizeGizmoColor != value ) { m_windowResizeGizmoColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_headerButtonsColor = Color.white;
|
||||
public Color HeaderButtonsColor
|
||||
{
|
||||
get { return m_headerButtonsColor; }
|
||||
set { if( m_headerButtonsColor != value ) { m_headerButtonsColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Sprite m_windowResizeGizmo;
|
||||
public Sprite WindowResizeGizmo
|
||||
{
|
||||
get { return m_windowResizeGizmo; }
|
||||
set { if( m_windowResizeGizmo != value ) { m_windowResizeGizmo = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Sprite m_headerBackButton;
|
||||
public Sprite HeaderBackButton
|
||||
{
|
||||
get { return m_headerBackButton; }
|
||||
set { if( m_headerBackButton != value ) { m_headerBackButton = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Sprite m_headerForwardButton;
|
||||
public Sprite HeaderForwardButton
|
||||
{
|
||||
get { return m_headerForwardButton; }
|
||||
set { if( m_headerForwardButton != value ) { m_headerForwardButton = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Sprite m_headerUpButton;
|
||||
public Sprite HeaderUpButton
|
||||
{
|
||||
get { return m_headerUpButton; }
|
||||
set { if( m_headerUpButton != value ) { m_headerUpButton = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Sprite m_headerContextMenuButton;
|
||||
public Sprite HeaderContextMenuButton
|
||||
{
|
||||
get { return m_headerContextMenuButton; }
|
||||
set { if( m_headerContextMenuButton != value ) { m_headerContextMenuButton = value; m_version++; } }
|
||||
}
|
||||
|
||||
[Header( "Input Fields" )]
|
||||
[SerializeField]
|
||||
private Color m_inputFieldNormalBackgroundColor = Color.white;
|
||||
public Color InputFieldNormalBackgroundColor
|
||||
{
|
||||
get { return m_inputFieldNormalBackgroundColor; }
|
||||
set { if( m_inputFieldNormalBackgroundColor != value ) { m_inputFieldNormalBackgroundColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_inputFieldInvalidBackgroundColor = Color.red;
|
||||
public Color InputFieldInvalidBackgroundColor
|
||||
{
|
||||
get { return m_inputFieldInvalidBackgroundColor; }
|
||||
set { if( m_inputFieldInvalidBackgroundColor != value ) { m_inputFieldInvalidBackgroundColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_inputFieldTextColor = Color.black;
|
||||
public Color InputFieldTextColor
|
||||
{
|
||||
get { return m_inputFieldTextColor; }
|
||||
set { if( m_inputFieldTextColor != value ) { m_inputFieldTextColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_inputFieldPlaceholderTextColor = new Color( 0f, 0f, 0f, 0.5f );
|
||||
public Color InputFieldPlaceholderTextColor
|
||||
{
|
||||
get { return m_inputFieldPlaceholderTextColor; }
|
||||
set { if( m_inputFieldPlaceholderTextColor != value ) { m_inputFieldPlaceholderTextColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_inputFieldSelectedTextColor = Color.blue;
|
||||
public Color InputFieldSelectedTextColor
|
||||
{
|
||||
get { return m_inputFieldSelectedTextColor; }
|
||||
set { if( m_inputFieldSelectedTextColor != value ) { m_inputFieldSelectedTextColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_inputFieldCaretColor = Color.black;
|
||||
public Color InputFieldCaretColor
|
||||
{
|
||||
get { return m_inputFieldCaretColor; }
|
||||
set { if( m_inputFieldCaretColor != value ) { m_inputFieldCaretColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Sprite m_inputFieldBackground;
|
||||
public Sprite InputFieldBackground
|
||||
{
|
||||
get { return m_inputFieldBackground; }
|
||||
set { if( m_inputFieldBackground != value ) { m_inputFieldBackground = value; m_version++; } }
|
||||
}
|
||||
|
||||
[Header( "Buttons" )]
|
||||
[SerializeField]
|
||||
private Color m_buttonColor = Color.white;
|
||||
public Color ButtonColor
|
||||
{
|
||||
get { return m_buttonColor; }
|
||||
set { if( m_buttonColor != value ) { m_buttonColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_buttonTextColor = Color.black;
|
||||
public Color ButtonTextColor
|
||||
{
|
||||
get { return m_buttonTextColor; }
|
||||
set { if( m_buttonTextColor != value ) { m_buttonTextColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Sprite m_buttonBackground;
|
||||
public Sprite ButtonBackground
|
||||
{
|
||||
get { return m_buttonBackground; }
|
||||
set { if( m_buttonBackground != value ) { m_buttonBackground = value; m_version++; } }
|
||||
}
|
||||
|
||||
[Header( "Dropdowns" )]
|
||||
[SerializeField]
|
||||
private Color m_dropdownColor = Color.white;
|
||||
public Color DropdownColor
|
||||
{
|
||||
get { return m_dropdownColor; }
|
||||
set { if( m_dropdownColor != value ) { m_dropdownColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_dropdownTextColor = Color.black;
|
||||
public Color DropdownTextColor
|
||||
{
|
||||
get { return m_dropdownTextColor; }
|
||||
set { if( m_dropdownTextColor != value ) { m_dropdownTextColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_dropdownArrowColor = Color.black;
|
||||
public Color DropdownArrowColor
|
||||
{
|
||||
get { return m_dropdownArrowColor; }
|
||||
set { if( m_dropdownArrowColor != value ) { m_dropdownArrowColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_dropdownCheckmarkColor = Color.black;
|
||||
public Color DropdownCheckmarkColor
|
||||
{
|
||||
get { return m_dropdownCheckmarkColor; }
|
||||
set { if( m_dropdownCheckmarkColor != value ) { m_dropdownCheckmarkColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Sprite m_dropdownBackground;
|
||||
public Sprite DropdownBackground
|
||||
{
|
||||
get { return m_dropdownBackground; }
|
||||
set { if( m_dropdownBackground != value ) { m_dropdownBackground = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Sprite m_dropdownArrow;
|
||||
public Sprite DropdownArrow
|
||||
{
|
||||
get { return m_dropdownArrow; }
|
||||
set { if( m_dropdownArrow != value ) { m_dropdownArrow = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Sprite m_dropdownCheckmark;
|
||||
public Sprite DropdownCheckmark
|
||||
{
|
||||
get { return m_dropdownCheckmark; }
|
||||
set { if( m_dropdownCheckmark != value ) { m_dropdownCheckmark = value; m_version++; } }
|
||||
}
|
||||
|
||||
[Header( "Toggles" )]
|
||||
[SerializeField]
|
||||
private Color m_toggleColor = Color.white;
|
||||
public Color ToggleColor
|
||||
{
|
||||
get { return m_toggleColor; }
|
||||
set { if( m_toggleColor != value ) { m_toggleColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_toggleTextColor = Color.black;
|
||||
public Color ToggleTextColor
|
||||
{
|
||||
get { return m_toggleTextColor; }
|
||||
set { if( m_toggleTextColor != value ) { m_toggleTextColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_toggleCheckmarkColor = Color.black;
|
||||
public Color ToggleCheckmarkColor
|
||||
{
|
||||
get { return m_toggleCheckmarkColor; }
|
||||
set { if( m_toggleCheckmarkColor != value ) { m_toggleCheckmarkColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Sprite m_toggleBackground;
|
||||
public Sprite ToggleBackground
|
||||
{
|
||||
get { return m_toggleBackground; }
|
||||
set { if( m_toggleBackground != value ) { m_toggleBackground = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Sprite m_toggleCheckmark;
|
||||
public Sprite ToggleCheckmark
|
||||
{
|
||||
get { return m_toggleCheckmark; }
|
||||
set { if( m_toggleCheckmark != value ) { m_toggleCheckmark = value; m_version++; } }
|
||||
}
|
||||
|
||||
[Header( "Scrollbars" )]
|
||||
[SerializeField]
|
||||
private Color m_scrollbarBackgroundColor = Color.grey;
|
||||
public Color ScrollbarBackgroundColor
|
||||
{
|
||||
get { return m_scrollbarBackgroundColor; }
|
||||
set { if( m_scrollbarBackgroundColor != value ) { m_scrollbarBackgroundColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_scrollbarColor = Color.black;
|
||||
public Color ScrollbarColor
|
||||
{
|
||||
get { return m_scrollbarColor; }
|
||||
set { if( m_scrollbarColor != value ) { m_scrollbarColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[Header( "Files" )]
|
||||
[SerializeField]
|
||||
private float m_fileHeight = 30f;
|
||||
public float FileHeight
|
||||
{
|
||||
get { return m_fileHeight; }
|
||||
set { if( m_fileHeight != value ) { m_fileHeight = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private float m_fileIconsPadding = 6f;
|
||||
public float FileIconsPadding
|
||||
{
|
||||
get { return m_fileIconsPadding; }
|
||||
set { if( m_fileIconsPadding != value ) { m_fileIconsPadding = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_fileNormalBackgroundColor = Color.clear;
|
||||
public Color FileNormalBackgroundColor
|
||||
{
|
||||
get { return m_fileNormalBackgroundColor; }
|
||||
set { if( m_fileNormalBackgroundColor != value ) { m_fileNormalBackgroundColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_fileAlternatingBackgroundColor = Color.clear;
|
||||
public Color FileAlternatingBackgroundColor
|
||||
{
|
||||
get { return m_fileAlternatingBackgroundColor; }
|
||||
set { if( m_fileAlternatingBackgroundColor != value ) { m_fileAlternatingBackgroundColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_fileHoveredBackgroundColor = Color.cyan;
|
||||
public Color FileHoveredBackgroundColor
|
||||
{
|
||||
get { return m_fileHoveredBackgroundColor; }
|
||||
set { if( m_fileHoveredBackgroundColor != value ) { m_fileHoveredBackgroundColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_fileSelectedBackgroundColor = Color.blue;
|
||||
public Color FileSelectedBackgroundColor
|
||||
{
|
||||
get { return m_fileSelectedBackgroundColor; }
|
||||
set { if( m_fileSelectedBackgroundColor != value ) { m_fileSelectedBackgroundColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_fileNormalTextColor = Color.black;
|
||||
public Color FileNormalTextColor
|
||||
{
|
||||
get { return m_fileNormalTextColor; }
|
||||
set { if( m_fileNormalTextColor != value ) { m_fileNormalTextColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_fileSelectedTextColor = Color.black;
|
||||
public Color FileSelectedTextColor
|
||||
{
|
||||
get { return m_fileSelectedTextColor; }
|
||||
set { if( m_fileSelectedTextColor != value ) { m_fileSelectedTextColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[Header( "File Icons" )]
|
||||
[SerializeField]
|
||||
private Sprite m_folderIcon;
|
||||
public Sprite FolderIcon
|
||||
{
|
||||
get { return m_folderIcon; }
|
||||
set { if( m_folderIcon != value ) { m_folderIcon = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Sprite m_driveIcon;
|
||||
public Sprite DriveIcon
|
||||
{
|
||||
get { return m_driveIcon; }
|
||||
set { if( m_driveIcon != value ) { m_driveIcon = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Sprite m_defaultFileIcon;
|
||||
public Sprite DefaultFileIcon
|
||||
{
|
||||
get { return m_defaultFileIcon; }
|
||||
set { if( m_defaultFileIcon != value ) { m_defaultFileIcon = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private FiletypeIcon[] m_filetypeIcons;
|
||||
public FiletypeIcon[] FiletypeIcons
|
||||
{
|
||||
get { return m_filetypeIcons; }
|
||||
set
|
||||
{
|
||||
if( m_filetypeIcons != value )
|
||||
{
|
||||
m_filetypeIcons = value;
|
||||
initializedFiletypeIcons = false;
|
||||
m_version++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[NonSerialized] // Never save this value during domain reload (it's sometimes saved even though it's private)
|
||||
private bool initializedFiletypeIcons = false;
|
||||
private Dictionary<string, Sprite> filetypeToIcon;
|
||||
|
||||
[NonSerialized]
|
||||
private bool m_allIconExtensionsHaveSingleSuffix = true;
|
||||
public bool AllIconExtensionsHaveSingleSuffix
|
||||
{
|
||||
get
|
||||
{
|
||||
if( !initializedFiletypeIcons )
|
||||
InitializeFiletypeIcons();
|
||||
|
||||
return m_allIconExtensionsHaveSingleSuffix;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Sprite m_fileMultiSelectionToggleOffIcon;
|
||||
public Sprite FileMultiSelectionToggleOffIcon
|
||||
{
|
||||
get { return m_fileMultiSelectionToggleOffIcon; }
|
||||
set { if( m_fileMultiSelectionToggleOffIcon != value ) { m_fileMultiSelectionToggleOffIcon = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Sprite m_fileMultiSelectionToggleOnIcon;
|
||||
public Sprite FileMultiSelectionToggleOnIcon
|
||||
{
|
||||
get { return m_fileMultiSelectionToggleOnIcon; }
|
||||
set { if( m_fileMultiSelectionToggleOnIcon != value ) { m_fileMultiSelectionToggleOnIcon = value; m_version++; } }
|
||||
}
|
||||
|
||||
[Header( "Context Menu" )]
|
||||
[SerializeField]
|
||||
private Color m_contextMenuBackgroundColor = Color.grey;
|
||||
public Color ContextMenuBackgroundColor
|
||||
{
|
||||
get { return m_contextMenuBackgroundColor; }
|
||||
set { if( m_contextMenuBackgroundColor != value ) { m_contextMenuBackgroundColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_contextMenuTextColor = Color.black;
|
||||
public Color ContextMenuTextColor
|
||||
{
|
||||
get { return m_contextMenuTextColor; }
|
||||
set { if( m_contextMenuTextColor != value ) { m_contextMenuTextColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Color m_contextMenuSeparatorColor = Color.black;
|
||||
public Color ContextMenuSeparatorColor
|
||||
{
|
||||
get { return m_contextMenuSeparatorColor; }
|
||||
set { if( m_contextMenuSeparatorColor != value ) { m_contextMenuSeparatorColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[Header( "Popup Panels" )]
|
||||
[SerializeField, UnityEngine.Serialization.FormerlySerializedAs( "m_deletePanelBackgroundColor" )]
|
||||
private Color m_popupPanelsBackgroundColor = Color.grey;
|
||||
public Color PopupPanelsBackgroundColor
|
||||
{
|
||||
get { return m_popupPanelsBackgroundColor; }
|
||||
set { if( m_popupPanelsBackgroundColor != value ) { m_popupPanelsBackgroundColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField, UnityEngine.Serialization.FormerlySerializedAs( "m_deletePanelTextColor" )]
|
||||
private Color m_popupPanelsTextColor = Color.black;
|
||||
public Color PopupPanelsTextColor
|
||||
{
|
||||
get { return m_popupPanelsTextColor; }
|
||||
set { if( m_popupPanelsTextColor != value ) { m_popupPanelsTextColor = value; m_version++; } }
|
||||
}
|
||||
|
||||
[SerializeField, UnityEngine.Serialization.FormerlySerializedAs( "m_deletePanelBackground" )]
|
||||
private Sprite m_popupPanelsBackground;
|
||||
public Sprite PopupPanelsBackground
|
||||
{
|
||||
get { return m_popupPanelsBackground; }
|
||||
set { if( m_popupPanelsBackground != value ) { m_popupPanelsBackground = value; m_version++; } }
|
||||
}
|
||||
#pragma warning restore 0649
|
||||
|
||||
public void ApplyTo( Text text, Color textColor )
|
||||
{
|
||||
text.color = textColor;
|
||||
text.font = m_font;
|
||||
text.fontSize = m_fontSize;
|
||||
}
|
||||
|
||||
public void ApplyTo( InputField inputField )
|
||||
{
|
||||
inputField.image.color = m_inputFieldNormalBackgroundColor;
|
||||
inputField.image.sprite = m_inputFieldBackground;
|
||||
inputField.selectionColor = m_inputFieldSelectedTextColor;
|
||||
inputField.caretColor = m_inputFieldCaretColor;
|
||||
|
||||
ApplyTo( inputField.textComponent, m_inputFieldTextColor );
|
||||
if( inputField.placeholder as Text )
|
||||
ApplyTo( (Text) inputField.placeholder, m_inputFieldPlaceholderTextColor );
|
||||
}
|
||||
|
||||
public void ApplyTo( Button button )
|
||||
{
|
||||
button.image.color = m_buttonColor;
|
||||
button.image.sprite = m_buttonBackground;
|
||||
|
||||
ApplyTo( button.GetComponentInChildren<Text>(), m_buttonTextColor );
|
||||
}
|
||||
|
||||
public void ApplyTo( Dropdown dropdown )
|
||||
{
|
||||
dropdown.image.color = m_dropdownColor;
|
||||
dropdown.image.sprite = m_dropdownBackground;
|
||||
dropdown.template.GetComponent<Image>().color = m_dropdownColor;
|
||||
|
||||
Image dropdownArrow = dropdown.transform.Find( "Arrow" ).GetComponent<Image>();
|
||||
dropdownArrow.color = m_dropdownArrowColor;
|
||||
dropdownArrow.sprite = m_dropdownArrow;
|
||||
|
||||
ApplyTo( dropdown.captionText, m_dropdownTextColor );
|
||||
ApplyTo( dropdown.itemText, m_dropdownTextColor );
|
||||
|
||||
Transform dropdownItem = dropdown.itemText.transform.parent;
|
||||
dropdownItem.Find( "Item Background" ).GetComponent<Image>().color = m_dropdownColor;
|
||||
|
||||
Image dropdownCheckmark = dropdownItem.Find( "Item Checkmark" ).GetComponent<Image>();
|
||||
dropdownCheckmark.color = m_dropdownCheckmarkColor;
|
||||
dropdownCheckmark.sprite = m_dropdownCheckmark;
|
||||
}
|
||||
|
||||
public void ApplyTo( Toggle toggle )
|
||||
{
|
||||
toggle.image.color = m_toggleColor;
|
||||
toggle.image.sprite = m_toggleBackground;
|
||||
toggle.graphic.color = m_toggleCheckmarkColor;
|
||||
( (Image) toggle.graphic ).sprite = m_toggleCheckmark;
|
||||
|
||||
ApplyTo( toggle.GetComponentInChildren<Text>(), m_toggleTextColor );
|
||||
}
|
||||
|
||||
public void ApplyTo( Scrollbar scrollbar )
|
||||
{
|
||||
scrollbar.GetComponent<Image>().color = m_scrollbarBackgroundColor;
|
||||
scrollbar.image.color = m_scrollbarColor;
|
||||
}
|
||||
|
||||
public Sprite GetIconForFileEntry( FileSystemEntry fileInfo, bool extensionMayHaveMultipleSuffixes )
|
||||
{
|
||||
if( !initializedFiletypeIcons )
|
||||
InitializeFiletypeIcons();
|
||||
|
||||
Sprite icon;
|
||||
if( fileInfo.IsDirectory )
|
||||
return m_folderIcon;
|
||||
else if( filetypeToIcon.TryGetValue( fileInfo.Extension, out icon ) )
|
||||
return icon;
|
||||
else if( extensionMayHaveMultipleSuffixes )
|
||||
{
|
||||
for( int i = 0; i < m_filetypeIcons.Length; i++ )
|
||||
{
|
||||
if( fileInfo.Extension.EndsWith( m_filetypeIcons[i].extension, StringComparison.Ordinal ) )
|
||||
{
|
||||
filetypeToIcon[fileInfo.Extension] = m_filetypeIcons[i].icon;
|
||||
return m_filetypeIcons[i].icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filetypeToIcon[fileInfo.Extension] = m_defaultFileIcon;
|
||||
return m_defaultFileIcon;
|
||||
}
|
||||
|
||||
private void InitializeFiletypeIcons()
|
||||
{
|
||||
initializedFiletypeIcons = true;
|
||||
|
||||
if( filetypeToIcon == null )
|
||||
filetypeToIcon = new Dictionary<string, Sprite>( 128 );
|
||||
else
|
||||
filetypeToIcon.Clear();
|
||||
|
||||
m_allIconExtensionsHaveSingleSuffix = true;
|
||||
|
||||
for( int i = 0; i < m_filetypeIcons.Length; i++ )
|
||||
{
|
||||
m_filetypeIcons[i].extension = m_filetypeIcons[i].extension.ToLowerInvariant();
|
||||
if( m_filetypeIcons[i].extension[0] != '.' )
|
||||
m_filetypeIcons[i].extension = "." + m_filetypeIcons[i].extension;
|
||||
|
||||
filetypeToIcon[m_filetypeIcons[i].extension] = m_filetypeIcons[i].icon;
|
||||
|
||||
m_allIconExtensionsHaveSingleSuffix &= ( m_filetypeIcons[i].extension.LastIndexOf( '.' ) == 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Plugins/SimpleFileBrowser/Scripts/UISkin.cs.meta
Normal file
12
Assets/Plugins/SimpleFileBrowser/Scripts/UISkin.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66bc3ce4885990c40a88f80fe0ad0101
|
||||
timeCreated: 1634711050
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user