1
This commit is contained in:
@@ -228,9 +228,24 @@ namespace BITKit.Console
|
||||
_commandSelector.SetMethods(null);
|
||||
}
|
||||
}
|
||||
private async void OnKeyDown(KeyDownEvent keyDownEvent)
|
||||
private bool _stopNextFrame;
|
||||
private void OnKeyDown(KeyDownEvent keyDownEvent)
|
||||
{
|
||||
var nextStop=true;
|
||||
if (_stopNextFrame)
|
||||
{
|
||||
keyDownEvent.StopPropagation();
|
||||
keyDownEvent.PreventDefault();
|
||||
_stopNextFrame = false;
|
||||
}
|
||||
|
||||
|
||||
if (keyDownEvent.keyCode is KeyCode.BackQuote)
|
||||
{
|
||||
keyDownEvent.StopPropagation();
|
||||
keyDownEvent.PreventDefault();
|
||||
_stopNextFrame = true;
|
||||
return;
|
||||
}
|
||||
switch (keyDownEvent.keyCode)
|
||||
{
|
||||
case KeyCode.Return:
|
||||
@@ -240,35 +255,30 @@ namespace BITKit.Console
|
||||
|
||||
_textField.SetValueWithoutNotify(string.Empty);
|
||||
|
||||
await UniTask.NextFrame();
|
||||
|
||||
_textField.Blur();
|
||||
|
||||
_textField.Focus();
|
||||
|
||||
BITCommands.Excute(cmd);
|
||||
|
||||
_commandSelector.SetMethods(null);
|
||||
|
||||
keyDownEvent.StopPropagation();
|
||||
keyDownEvent.PreventDefault();
|
||||
_stopNextFrame = true;
|
||||
break;
|
||||
case KeyCode.Tab:
|
||||
keyDownEvent.StopPropagation();
|
||||
keyDownEvent.PreventDefault();
|
||||
break;
|
||||
case KeyCode.DownArrow when string.IsNullOrEmpty(_textField.text) is false:
|
||||
_commandSelector.Index-=1;
|
||||
keyDownEvent.StopPropagation();
|
||||
keyDownEvent.PreventDefault();
|
||||
break;
|
||||
case KeyCode.UpArrow when string.IsNullOrEmpty(_textField.text) is false:
|
||||
_commandSelector.Index+=1;
|
||||
break;
|
||||
default:
|
||||
nextStop = false;
|
||||
keyDownEvent.StopPropagation();
|
||||
keyDownEvent.PreventDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
if (nextStop)
|
||||
{
|
||||
keyDownEvent.StopPropagation();
|
||||
keyDownEvent.PreventDefault();
|
||||
}
|
||||
}
|
||||
private async void LogCallback(string condition, string stackTrace, LogType type)
|
||||
{
|
||||
|
@@ -40,6 +40,7 @@ namespace BITKit
|
||||
}
|
||||
public sealed class UnityLogger<T>:ILogger<T>
|
||||
{
|
||||
[HideInCallstack]
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
switch (logLevel)
|
||||
@@ -56,7 +57,7 @@ namespace BITKit
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Debug.Log($"<color=cyan>{typeof(T).Name}</color>:{state.ToString()}");
|
||||
Debug.Log($"<b>{typeof(T).Name}</b>:{state.ToString()}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@@ -75,7 +75,7 @@ namespace BITKit
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
LateUpdateTick?.Invoke(Time.deltaTime);
|
||||
LateUpdateTick?.Invoke(Time.deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -5,6 +5,7 @@ using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine.InputSystem;
|
||||
using BITKit.Mod;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace BITKit.UX.Internal
|
||||
@@ -111,7 +112,7 @@ namespace BITKit.UX
|
||||
private readonly IUXService _uxService;
|
||||
private VisualElement _root;
|
||||
private VisualElement _container;
|
||||
private bool _isInitialized = false;
|
||||
private readonly UniTaskCompletionSource _waitUntilInitialized = new();
|
||||
public UXContextMenu(IUXService uxService)
|
||||
{
|
||||
_uxService = uxService;
|
||||
@@ -144,11 +145,14 @@ namespace BITKit.UX
|
||||
{
|
||||
Close();
|
||||
});
|
||||
_isInitialized = true;
|
||||
|
||||
_waitUntilInitialized.TrySetResult();
|
||||
|
||||
Close();
|
||||
}
|
||||
public void Create(ContextMenuBuilder builder)
|
||||
public async void Create(ContextMenuBuilder builder)
|
||||
{
|
||||
await _waitUntilInitialized.Task;
|
||||
var pos = Mouse.current.position.ReadValue();
|
||||
pos.y = Screen.height - pos.y;
|
||||
pos = RuntimePanelUtils.ScreenToPanel(_root.panel, pos);
|
||||
@@ -166,9 +170,9 @@ namespace BITKit.UX
|
||||
}
|
||||
builder.OnExecuted += Close;
|
||||
}
|
||||
private void Close()
|
||||
private async void Close()
|
||||
{
|
||||
if(_isInitialized is false)return;
|
||||
await _waitUntilInitialized.Task;
|
||||
_container.Clear();
|
||||
_root.SetActive(false);
|
||||
}
|
||||
|
@@ -106,6 +106,7 @@ namespace BITKit.UX
|
||||
var tabName = split[i];
|
||||
var index = i;
|
||||
var button = _buttons[i] = this.Create<Button>();
|
||||
button.AddToClassList("v"+i);
|
||||
button.text = tabName;
|
||||
button.focusable = allowFocus;
|
||||
button.clicked += () => CurrentTab = index;
|
||||
|
@@ -25,50 +25,48 @@ namespace BITKit.UX
|
||||
public new class UxmlFactory : UxmlFactory<TabContainer, UxmlTraits> { }
|
||||
public TabContainer()
|
||||
{
|
||||
RegisterCallback<AttachToPanelEvent>(OnAttachToPanel);
|
||||
RegisterCallback<DetachFromPanelEvent>(OnDetachFromPanel);
|
||||
RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
|
||||
RegisterCallback<AttachToPanelEvent>(RebuildOnEvent);
|
||||
RegisterCallback<DetachFromPanelEvent>(RebuildOnEvent);
|
||||
RegisterCallback<GeometryChangedEvent>(RebuildOnEvent);
|
||||
RegisterCallback<BlurEvent>(RebuildOnEvent);
|
||||
RegisterCallback<FocusEvent>(RebuildOnEvent);
|
||||
}
|
||||
|
||||
private void OnGeometryChanged(GeometryChangedEvent evt)
|
||||
{
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
public string TabPath
|
||||
{
|
||||
get=>_tabPath;
|
||||
set
|
||||
{
|
||||
_tabPath = value;
|
||||
Rebuild();
|
||||
}
|
||||
}
|
||||
private string _tabPath;
|
||||
private TabBar _tabBar;
|
||||
private int _index;
|
||||
private void OnDetachFromPanel(DetachFromPanelEvent evt)
|
||||
private void RebuildOnEvent<T>(T evt)
|
||||
{
|
||||
Rebuild();
|
||||
}
|
||||
private void OnAttachToPanel(AttachToPanelEvent evt)
|
||||
{
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
private void Rebuild()
|
||||
{
|
||||
if (_tabBar is not null)
|
||||
{
|
||||
_tabBar.OnTabChanged -= OnTabChanged;
|
||||
}
|
||||
_tabBar = panel.visualTree.Q<TabBar>(TabPath);
|
||||
var p = parent;
|
||||
while (p is not null)
|
||||
{
|
||||
_tabBar = p.Q<TabBar>(TabPath);
|
||||
if (_tabBar is not null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
p = p.parent;
|
||||
}
|
||||
if (_tabBar is not null)
|
||||
{
|
||||
_tabBar.OnTabChanged += OnTabChanged;
|
||||
}
|
||||
if (_index <=0)
|
||||
{
|
||||
_index = 1;
|
||||
_index = _tabBar.CurrentTab;
|
||||
}
|
||||
OnTabChanged(_index);
|
||||
}
|
||||
@@ -76,7 +74,7 @@ namespace BITKit.UX
|
||||
private void OnTabChanged(int obj)
|
||||
{
|
||||
_index = obj;
|
||||
if (childCount <= 0) return;
|
||||
if (childCount < 0) return;
|
||||
for (var i = 0; i < childCount; i++)
|
||||
{
|
||||
var visualElement = this[i];
|
||||
|
@@ -21,16 +21,10 @@ namespace BITKit.UX
|
||||
private const string TemplatePath = "ux_mod_service_template";
|
||||
public override bool AllowCursor => true;
|
||||
|
||||
public UXModService(IUXService uxService) : base(uxService)
|
||||
{
|
||||
ModService.OnModInstalled+=OnModInstalled;
|
||||
ModService.OnModUnInstalled+=OnModUnInstalled;
|
||||
ModService.OnModLoaded+=OnModLoaded;
|
||||
ModService.OnModUnLoaded+=OnModUnLoaded;
|
||||
ModService.OnLocked+=OnLocked;
|
||||
}
|
||||
|
||||
private VisualTreeAsset _modTemplate;
|
||||
|
||||
[UXBindPath("open_folder-button")]
|
||||
private Button _openFolderButton;
|
||||
[UXBindPath("open-mod-button")]
|
||||
private Button _openModButton;
|
||||
[UXBindPath("mods-container")]
|
||||
@@ -43,22 +37,27 @@ namespace BITKit.UX
|
||||
private Label _modDescriptionLabel;
|
||||
[UXBindPath("reload-mod-button",true)]
|
||||
private Button _reloadModButton;
|
||||
[UXBindPath("install-roslyn-fill")]
|
||||
private VisualElement _installRoslynFill;
|
||||
|
||||
private readonly ConcurrentDictionary<string,VisualElement> _modContainers=new();
|
||||
public UXModService(IUXService uxService) : base(uxService)
|
||||
{
|
||||
ModService.OnModInstalled+=OnModInstalled;
|
||||
ModService.OnModUnInstalled+=OnModUnInstalled;
|
||||
ModService.OnModLoaded+=OnModLoaded;
|
||||
ModService.OnModUnLoaded+=OnModUnLoaded;
|
||||
ModService.IsBusy.AddListener(OnLocked);
|
||||
|
||||
private void OnLocked(bool obj)
|
||||
{
|
||||
RootVisualElement?.SetEnabled(!obj);
|
||||
OnInitiatedAsync += InitializeAsync;
|
||||
}
|
||||
public override async UniTask EntryAsync()
|
||||
|
||||
private async UniTask InitializeAsync()
|
||||
{
|
||||
await base.EntryAsync();
|
||||
_installRoslynFill.style.width = 0;
|
||||
|
||||
_modTemplate =await ModService.LoadAsset<VisualTreeAsset>(TemplatePath);
|
||||
UXUtils.Inject(this);
|
||||
if (_openModButton is not null)
|
||||
{
|
||||
_openModButton.clicked += OpenMod;
|
||||
}
|
||||
|
||||
if (_returnButton is not null)
|
||||
{
|
||||
@@ -79,7 +78,22 @@ namespace BITKit.UX
|
||||
await UniTask.SwitchToMainThread();
|
||||
_reloadModButton.SetEnabled(true);
|
||||
};
|
||||
|
||||
_openFolderButton.clicked += () =>
|
||||
{
|
||||
new BITApp.OpenPath()
|
||||
{
|
||||
path = Path.Combine(Environment.CurrentDirectory, "Mods")
|
||||
}.Execute();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private void OnLocked(bool obj)
|
||||
{
|
||||
RootVisualElement?.SetEnabled(!obj);
|
||||
}
|
||||
|
||||
private async void OnModUnInstalled(IMod obj)
|
||||
{
|
||||
await UniTask.SwitchToMainThread();
|
||||
@@ -117,78 +131,6 @@ namespace BITKit.UX
|
||||
var container = _modContainers.GetOrAdd(obj.Name,_=> Create(obj));
|
||||
container.Get<Toggle>().SetValueWithoutNotify(true);
|
||||
}
|
||||
|
||||
private static void OpenMod()
|
||||
{
|
||||
BIT4Log.Log("正在打开选择文件对话框");
|
||||
#if UNITY_EDITOR
|
||||
new Thread(OpenModInternal).Start();
|
||||
#else
|
||||
OpenModInternal();
|
||||
#endif
|
||||
|
||||
return;
|
||||
void OpenModInternal()
|
||||
{
|
||||
#if UNITY_5_3_OR_NEWER && UNITY_WINDOW
|
||||
BIT4Log.Log<UXModService>("已进入文件对话框线程");
|
||||
new FileBrowser().OpenFileBrowser(new BrowserProperties()
|
||||
{
|
||||
//filterIndex = 0,
|
||||
//filter = "C Sharp files (*.cs)|*.cs |Dll files (*.dll)|*.dll",
|
||||
}, Filepath);
|
||||
#else
|
||||
throw new NotSupportedException($"{Application.platform} 不支持打开文件对话框");
|
||||
#endif
|
||||
return;
|
||||
async void Filepath(string path)
|
||||
{
|
||||
BIT4Log.Log<UXModService>("已选择文件:"+path);
|
||||
await BITApp.SwitchToMainThread();
|
||||
try
|
||||
{
|
||||
var file = new FileInfo(path);
|
||||
|
||||
switch (file.Extension)
|
||||
{
|
||||
case ".cs":
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
var code = await File.ReadAllTextAsync(path);
|
||||
var assembly = BITSharp.Compile(code);
|
||||
await ModService.Load(assembly,Path.GetDirectoryName(path));
|
||||
#else
|
||||
throw new NotSupportedException($"{Application.platform} 不支持编译C#代码");
|
||||
#endif
|
||||
break;
|
||||
case ".dll":
|
||||
var bytes = await File.ReadAllBytesAsync(path);
|
||||
await ModService.Load(Assembly.Load(bytes),Path.GetDirectoryName(path));
|
||||
break;
|
||||
case ".json" when file.Name is ModPackage.DefaultFileName:
|
||||
await ModService.LoadFromPackage(path);
|
||||
break;
|
||||
default:
|
||||
Alert.Print(new AlertMessage()
|
||||
{
|
||||
title = "加载失败",
|
||||
message = "不支持的文件类型"
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Alert.Print(new AlertMessage()
|
||||
{
|
||||
title = "加载失败",
|
||||
message = e.Message
|
||||
});
|
||||
BIT4Log.LogException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private VisualElement Create(IMod mod)
|
||||
{
|
||||
var container =_modsContainer.Create(_modTemplate);
|
||||
@@ -219,7 +161,7 @@ namespace BITKit.UX
|
||||
ModService.OnModUnInstalled-=OnModUnInstalled;
|
||||
ModService.OnModLoaded-=OnModLoaded;
|
||||
ModService.OnModUnLoaded-=OnModUnLoaded;
|
||||
ModService.OnLocked-=OnLocked;
|
||||
ModService.IsBusy.RemoveListener(OnLocked);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -18,7 +18,7 @@ using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace BITKit.UX
|
||||
{
|
||||
public abstract class UIToolKitPanel : IUXPanel
|
||||
public abstract class UIToolKitPanel : IUXPanel,IDisposable
|
||||
{
|
||||
public const string USSEntry = "transition_entry";
|
||||
public const string USSEntryAsync = "transition_entry_async";
|
||||
@@ -128,6 +128,7 @@ namespace BITKit.UX
|
||||
public virtual bool AllowCursor { get; }
|
||||
public virtual bool AllowInput { get; }
|
||||
public virtual string[] InitialUssClasses { get; } = Array.Empty<string>();
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
public bool IsEntered { get; set; }
|
||||
protected virtual void OnReturn()
|
||||
@@ -247,6 +248,12 @@ namespace BITKit.UX
|
||||
public virtual void OnTick(float deltaTime)
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
RootVisualElement?.RemoveFromHierarchy();
|
||||
IsDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@ namespace BITKit.UX
|
||||
protected readonly IUXService UXService;
|
||||
protected VisualElement RootVisualElement { get; set; }
|
||||
protected bool IsInitialized { get; private set; }
|
||||
protected readonly UniTaskCompletionSource WaitUntilInitialized = new();
|
||||
protected UIToolkitOverlay(IUXService uxService, CancellationTokenSource cancellationToken)
|
||||
{
|
||||
UXService = uxService;
|
||||
@@ -40,6 +41,8 @@ namespace BITKit.UX
|
||||
|
||||
UXUtils.Inject(this);
|
||||
IsInitialized = true;
|
||||
|
||||
WaitUntilInitialized.TrySetResult();
|
||||
}
|
||||
public virtual void Dispose()
|
||||
{
|
||||
|
@@ -71,6 +71,7 @@ namespace BITKit.UX
|
||||
/// </summary>
|
||||
public static void ClearHistory() => History.Clear();
|
||||
|
||||
public string SettingsPath { get; set; } = "ux_panel_settings";
|
||||
public object Root { get; private set; }
|
||||
public async UniTask InitializeAsync()
|
||||
{
|
||||
@@ -86,7 +87,7 @@ Object.Destroy(gameObject);
|
||||
var document = gameObject.AddComponent<UIDocument>();
|
||||
try
|
||||
{
|
||||
var panelSettings =await ModService.LoadAsset<PanelSettings>("ux_panel_settings");
|
||||
var panelSettings =await ModService.LoadAsset<PanelSettings>(SettingsPath);
|
||||
document.panelSettings = panelSettings;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@@ -102,7 +102,16 @@ namespace BITKit
|
||||
handle.VisualElement.RemoveFromHierarchy();
|
||||
_visibleHandle.RemoveElement(handle);
|
||||
}
|
||||
_visibleHandle.Invoke();
|
||||
|
||||
try
|
||||
{
|
||||
_visibleHandle.Invoke();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override async UniTask InitializeAsync()
|
||||
@@ -116,6 +125,8 @@ namespace BITKit
|
||||
|
||||
UXUtils.Inject(this);
|
||||
_container.Clear();
|
||||
|
||||
_visibleHandle.Invoke();
|
||||
|
||||
_initializationState = InitializationState.Initialized;
|
||||
}
|
||||
|
@@ -714,5 +714,48 @@ namespace BITKit
|
||||
|
||||
self.SetOpacity(visible ? 1 : 0);
|
||||
}
|
||||
|
||||
public static void ClearTooltipsOnPointerLeave(this VisualElement visualElement)
|
||||
{
|
||||
visualElement.RegisterCallback<PointerLeaveEvent>(OnLeave);
|
||||
return;
|
||||
|
||||
void OnLeave(PointerLeaveEvent evt)
|
||||
{
|
||||
if (string.IsNullOrEmpty(visualElement.tooltip) is false)
|
||||
visualElement.tooltip = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public static void BlinkingCursor(this TextField tf,int interval=1000)
|
||||
{
|
||||
tf.schedule.Execute(() =>
|
||||
{
|
||||
if(tf.ClassListContains("transparentCursor"))
|
||||
tf.RemoveFromClassList("transparentCursor");
|
||||
else
|
||||
tf.AddToClassList("transparentCursor");
|
||||
}).Every(interval);
|
||||
}
|
||||
public static Vector2 GetAbsolutePositionInUI(this VisualElement element)
|
||||
{
|
||||
var position = Vector2.zero;
|
||||
var currentElement = element;
|
||||
|
||||
// 遍历每一个父元素,并累计位置
|
||||
while (currentElement != null)
|
||||
{
|
||||
var style = currentElement.resolvedStyle;
|
||||
|
||||
// 累加该元素相对于父元素的位置
|
||||
position.x += style.left;
|
||||
position.y += style.top;
|
||||
|
||||
// 移动到父元素
|
||||
currentElement = currentElement.parent;
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user