514 lines
22 KiB
C#
514 lines
22 KiB
C#
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
using UnityEngine.Audio;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System;
|
|
using System.Threading.Tasks;
|
|
using OnlyScove.Scripts;
|
|
using Hallucinate.Audio;
|
|
using PrimeTween;
|
|
|
|
namespace Hallucinate.UI
|
|
{
|
|
public class SettingsController : BaseUIController
|
|
{
|
|
private VisualElement _sidebar;
|
|
private Label _tabTitle;
|
|
private ScrollView _content;
|
|
private Dictionary<string, Button> _tabButtons = new Dictionary<string, Button>();
|
|
private string _activeTab = "GENERAL";
|
|
|
|
// Advanced Mouse Metrics
|
|
private Label _mouseMetricsLabel;
|
|
|
|
// FPS State
|
|
private bool _fpsVisible;
|
|
|
|
// Hover Tracking for Arrow Key Slider Control
|
|
private Slider _hoveredSlider;
|
|
private Action<float> _hoveredOnChanged;
|
|
private float _sliderMin, _sliderMax;
|
|
|
|
// Osu-style Volume Overlay
|
|
private VisualElement _volumeContainer;
|
|
private VisualElement _masterRing;
|
|
private Label _masterVolLabel;
|
|
private Dictionary<string, (VisualElement ring, Label label)> _subRings = new Dictionary<string, (VisualElement, Label)>();
|
|
private string _hoveredSubVolume = null;
|
|
private float _masterVol = 80f;
|
|
private int _overlayActiveCount = 0;
|
|
|
|
public override void Initialize(VisualElement uxmlRoot, UIManager manager)
|
|
{
|
|
base.Initialize(uxmlRoot, manager);
|
|
|
|
_sidebar = root.Q<VisualElement>("Sidebar");
|
|
_tabTitle = root.Q<Label>("TabTitle");
|
|
_content = root.Q<ScrollView>("SettingsContent");
|
|
|
|
// Global Volume Catch - Use TrickleDown to catch events before they are consumed by children (like ScrollViews)
|
|
uiManager.Root.RegisterCallback<WheelEvent>(OnMouseWheel, TrickleDown.TrickleDown);
|
|
SetupHierarchicalVolumeOverlay();
|
|
|
|
root.RegisterCallback<PointerDownEvent>(evt => {
|
|
if (evt.target == root) uiManager.ToggleSettings();
|
|
});
|
|
|
|
// Keyboard navigation for sliders
|
|
root.RegisterCallback<KeyDownEvent>(OnKeyDown);
|
|
|
|
SetupTab("GeneralTab", "GENERAL");
|
|
SetupTab("VideoTab", "VIDEO");
|
|
SetupTab("SoundTab", "SOUND");
|
|
SetupTab("ControlTab", "CONTROL");
|
|
|
|
var closeBtn = root.Q<Button>("CloseSettingsBtn");
|
|
if (closeBtn != null) closeBtn.clicked += () => uiManager.ToggleSettings();
|
|
|
|
_masterVol = PlayerPrefs.GetFloat("MasterVolume", 80f);
|
|
|
|
SwitchTab("GENERAL");
|
|
}
|
|
|
|
private void SetupHierarchicalVolumeOverlay()
|
|
{
|
|
_volumeContainer = new VisualElement();
|
|
_volumeContainer.name = "GlobalVolumeOverlay";
|
|
_volumeContainer.style.position = Position.Absolute;
|
|
_volumeContainer.style.right = 50;
|
|
_volumeContainer.style.bottom = 50;
|
|
_volumeContainer.style.width = 300;
|
|
_volumeContainer.style.height = 300;
|
|
_volumeContainer.style.display = DisplayStyle.None;
|
|
_volumeContainer.pickingMode = PickingMode.Ignore;
|
|
|
|
// Add to UIManager root so it stays even when Settings is hidden
|
|
uiManager.Root.Add(_volumeContainer);
|
|
|
|
// Master Ring (Bottom Right)
|
|
_masterRing = CreateRing("Master", 120, cyan: true);
|
|
_masterRing.style.right = 0;
|
|
_masterRing.style.bottom = 0;
|
|
_masterVolLabel = _masterRing.Q<Label>();
|
|
_volumeContainer.Add(_masterRing);
|
|
|
|
// Sub Rings (Music, VFX, Player, UI)
|
|
string[] subs = { "MusicVolume", "VFXVolume", "PlayerVolume", "UIVolume" };
|
|
string[] shortNames = { "MUS", "VFX", "PLY", "UI" };
|
|
|
|
// Layout sub-rings in an arc around Master
|
|
for (int i = 0; i < subs.Length; i++)
|
|
{
|
|
var ring = CreateRing(shortNames[i], 70, false);
|
|
|
|
// Angle 0 = Top, Angle 90 = Left
|
|
float angle = (i * 30f) * Mathf.Deg2Rad;
|
|
float radius = 140f;
|
|
|
|
// right moves element to the LEFT, bottom moves element UP
|
|
// Starting from top (right=25, bottom=140+25) to left (right=140+25, bottom=25)
|
|
ring.style.right = 25 + Mathf.Sin(angle) * radius;
|
|
ring.style.bottom = 25 + Mathf.Cos(angle) * radius;
|
|
|
|
string key = subs[i];
|
|
ring.RegisterCallback<PointerEnterEvent>(evt => _hoveredSubVolume = key);
|
|
ring.RegisterCallback<PointerLeaveEvent>(evt => { if (_hoveredSubVolume == key) _hoveredSubVolume = null; });
|
|
ring.pickingMode = PickingMode.Position; // Allow hover detection
|
|
|
|
_subRings[key] = (ring, ring.Q<Label>());
|
|
_volumeContainer.Add(ring);
|
|
}
|
|
}
|
|
|
|
private VisualElement CreateRing(string text, float size, bool cyan)
|
|
{
|
|
var ring = new VisualElement();
|
|
ring.style.width = size;
|
|
ring.style.height = size;
|
|
ring.style.backgroundColor = new Color(0, 0, 0, 0.85f);
|
|
ring.style.borderTopLeftRadius = size / 2;
|
|
ring.style.borderTopRightRadius = size / 2;
|
|
ring.style.borderBottomLeftRadius = size / 2;
|
|
ring.style.borderBottomRightRadius = size / 2;
|
|
ring.style.borderTopWidth = 3;
|
|
ring.style.borderBottomWidth = 3;
|
|
ring.style.borderLeftWidth = 3;
|
|
ring.style.borderRightWidth = 3;
|
|
ring.style.borderTopColor = ring.style.borderBottomColor = ring.style.borderLeftColor = ring.style.borderRightColor = cyan ? Color.cyan : new Color(0.7f, 0.7f, 0.7f);
|
|
ring.style.justifyContent = Justify.Center;
|
|
ring.style.alignItems = Align.Center;
|
|
ring.style.position = Position.Absolute;
|
|
|
|
var label = new Label("80%");
|
|
label.style.color = Color.white;
|
|
label.style.fontSize = size * 0.25f;
|
|
label.style.unityFontStyleAndWeight = FontStyle.Bold;
|
|
ring.Add(label);
|
|
|
|
var title = new Label(text);
|
|
title.style.color = Color.gray;
|
|
title.style.fontSize = size * 0.15f;
|
|
title.style.position = Position.Absolute;
|
|
title.style.bottom = size * 0.15f;
|
|
ring.Add(title);
|
|
|
|
return ring;
|
|
}
|
|
|
|
private void OnMouseWheel(WheelEvent evt)
|
|
{
|
|
// Debug Log to see if event is even reaching here
|
|
Debug.Log($"[SettingsController] Mouse Wheel Detected. Settings Open: {uiManager.IsSettingsOpen}");
|
|
|
|
// Do not control volume if we are in MainMenu (unless Settings is explicitly open)
|
|
// Fix: Check if MainMenu is actually visible (Flex), not just exists in hierarchy
|
|
var mainMenuRoot = uiManager.Root.Q<VisualElement>("MainMenuRoot");
|
|
bool isMainMenuVisible = mainMenuRoot != null && mainMenuRoot.style.display == DisplayStyle.Flex;
|
|
|
|
if (!uiManager.IsSettingsOpen && isMainMenuVisible)
|
|
{
|
|
Debug.Log("[SettingsController] Volume control suppressed: Currently at Main Menu.");
|
|
return;
|
|
}
|
|
|
|
// Osu style volume control
|
|
_overlayActiveCount++;
|
|
ShowVolumeOverlay();
|
|
// ... rest of method unchanged
|
|
|
|
if (_hoveredSubVolume != null)
|
|
{
|
|
Debug.Log($"[SettingsController] Adjusting Sub Volume: {_hoveredSubVolume}");
|
|
UpdateSubVolume(_hoveredSubVolume, -evt.delta.y * 2f);
|
|
}
|
|
else if (_hoveredSlider != null && _activeTab == "SOUND")
|
|
{
|
|
// If hovering a slider in the Sound tab, adjust that
|
|
float currentVal = _hoveredSlider.value;
|
|
float step = (_sliderMax - _sliderMin) / 100f;
|
|
float newVal = Mathf.Clamp(currentVal - (evt.delta.y * step * 5f), _sliderMin, _sliderMax);
|
|
_hoveredSlider.value = newVal;
|
|
_hoveredOnChanged?.Invoke(newVal);
|
|
}
|
|
else
|
|
{
|
|
Debug.Log("[SettingsController] Adjusting Master Volume.");
|
|
UpdateMasterVolume(-evt.delta.y * 2f);
|
|
}
|
|
|
|
evt.StopPropagation();
|
|
}
|
|
|
|
private void UpdateMasterVolume(float delta)
|
|
{
|
|
_masterVol = Mathf.Clamp(_masterVol + delta, 0f, 100f);
|
|
PlayerPrefs.SetFloat("MasterVolume", _masterVol);
|
|
AudioManager.Instance?.SetVolume("MasterVolume", _masterVol);
|
|
_masterVolLabel.text = $"{Mathf.RoundToInt(_masterVol)}%";
|
|
|
|
// Refresh Sound Tab UI if visible
|
|
if (_activeTab == "SOUND") SwitchTab("SOUND");
|
|
}
|
|
|
|
private void UpdateSubVolume(string key, float delta)
|
|
{
|
|
float current = PlayerPrefs.GetFloat(key, 80f);
|
|
float newVal = Mathf.Clamp(current + delta, 0f, 100f);
|
|
PlayerPrefs.SetFloat(key, newVal);
|
|
AudioManager.Instance?.SetVolume(key, newVal);
|
|
|
|
if (_subRings.TryGetValue(key, out var data))
|
|
data.label.text = $"{Mathf.RoundToInt(newVal)}%";
|
|
|
|
if (_activeTab == "SOUND") SwitchTab("SOUND");
|
|
}
|
|
|
|
private async void ShowVolumeOverlay()
|
|
{
|
|
Debug.Log("[SettingsController] Showing Volume Overlay.");
|
|
// Ensure overlay is on top of other UI screens
|
|
_volumeContainer.BringToFront();
|
|
|
|
// CRITICAL: Ensure Virtual Cursor is ALWAYS on top of the volume rings
|
|
uiManager.Root.Q<VisualElement>("CursorLayer")?.BringToFront();
|
|
|
|
_volumeContainer.style.display = DisplayStyle.Flex;
|
|
_volumeContainer.style.opacity = 1f;
|
|
|
|
// Refresh all sub labels
|
|
foreach (var kvp in _subRings)
|
|
{
|
|
float val = PlayerPrefs.GetFloat(kvp.Key, 80f);
|
|
kvp.Value.label.text = $"{Mathf.RoundToInt(val)}%";
|
|
}
|
|
_masterVolLabel.text = $"{Mathf.RoundToInt(_masterVol)}%";
|
|
|
|
int currentId = _overlayActiveCount;
|
|
await Task.Delay(3000); // Wait 3s as requested
|
|
|
|
// Only fade out if no new scroll activity happened
|
|
if (currentId == _overlayActiveCount && _hoveredSubVolume == null)
|
|
{
|
|
Debug.Log("[SettingsController] Fading out Volume Overlay.");
|
|
Tween.Custom(1f, 0f, duration: 0.5f, onValueChange: val => _volumeContainer.style.opacity = val)
|
|
.OnComplete(() => {
|
|
if (_volumeContainer.style.opacity == 0f)
|
|
_volumeContainer.style.display = DisplayStyle.None;
|
|
});
|
|
}
|
|
}
|
|
|
|
private void SetupTab(string btnName, string tabId)
|
|
{
|
|
var btn = root.Q<Button>(btnName);
|
|
if (btn != null)
|
|
{
|
|
_tabButtons[tabId] = btn;
|
|
btn.clicked += () => SwitchTab(tabId);
|
|
}
|
|
}
|
|
|
|
private void SwitchTab(string tabId)
|
|
{
|
|
_activeTab = tabId;
|
|
_tabTitle.text = tabId;
|
|
|
|
foreach (var kvp in _tabButtons)
|
|
{
|
|
if (kvp.Key == tabId) kvp.Value.AddToClassList("active-tab");
|
|
else kvp.Value.RemoveFromClassList("active-tab");
|
|
}
|
|
|
|
_content.Clear();
|
|
_hoveredSlider = null;
|
|
|
|
switch (tabId)
|
|
{
|
|
case "GENERAL": RenderGeneralTab(); break;
|
|
case "VIDEO": RenderVideoTab(); break;
|
|
case "SOUND": RenderSoundTab(); break;
|
|
case "CONTROL": RenderControlTab(); break;
|
|
}
|
|
}
|
|
|
|
#region GENERAL TAB
|
|
private void RenderGeneralTab()
|
|
{
|
|
_content.Add(CreateSection("ACCOUNT"));
|
|
string username = PlayerPrefs.GetString("Username", "Guest");
|
|
var userRow = new VisualElement { style = { flexDirection = FlexDirection.Row, alignItems = Align.Center, marginBottom = 10 } };
|
|
var loggedInLabel = new Label("Logged in as: ");
|
|
loggedInLabel.AddToClassList("text-body");
|
|
userRow.Add(loggedInLabel);
|
|
userRow.Add(new Label(username) { style = { color = Color.cyan, marginLeft = 5, unityFontStyleAndWeight = FontStyle.Bold } });
|
|
_content.Add(userRow);
|
|
|
|
_content.Add(CreateSection("LANGUAGE"));
|
|
var langDropdown = new DropdownField(new List<string> { "English", "Tiếng Việt" },
|
|
LocalizationManager.Instance?.CurrentLanguage == "vi" ? 1 : 0);
|
|
langDropdown.AddToClassList("custom-dropdown");
|
|
langDropdown.RegisterValueChangedCallback(evt => {
|
|
LocalizationManager.Instance?.LoadLanguage(evt.newValue == "Tiếng Việt" ? "vi" : "en");
|
|
SwitchTab("GENERAL");
|
|
});
|
|
_content.Add(langDropdown);
|
|
|
|
_content.Add(CreateSection("UPDATES"));
|
|
var versionBox = new VisualElement { style = { flexDirection = FlexDirection.Row, alignItems = Align.Center } };
|
|
var versionLabel = new Label($"Version: {Application.version}");
|
|
versionLabel.AddToClassList("text-body");
|
|
versionBox.Add(versionLabel);
|
|
var checkBtn = new Button { text = "CHECK FOR UPDATES" };
|
|
checkBtn.AddToClassList("button-spring");
|
|
checkBtn.clicked += () => checkBtn.text = "UP TO DATE";
|
|
versionBox.Add(checkBtn);
|
|
_content.Add(versionBox);
|
|
|
|
_content.Add(CreateSection("CURSOR & MOUSE"));
|
|
_content.Add(CreateSliderWithInput("Cursor Size", 10, 150, PlayerPrefs.GetFloat("CursorSize", 40), val => PlayerPrefs.SetFloat("CursorSize", val)));
|
|
|
|
var trailToggle = new Toggle("Enable Cursor Trail") { value = PlayerPrefs.GetInt("CursorTrail", 1) == 1 };
|
|
trailToggle.RegisterValueChangedCallback(evt => PlayerPrefs.SetInt("CursorTrail", evt.newValue ? 1 : 0));
|
|
_content.Add(trailToggle);
|
|
|
|
var rippleToggle = new Toggle("Enable Ripple Effects") { value = PlayerPrefs.GetInt("CursorRipples", 1) == 1 };
|
|
rippleToggle.RegisterValueChangedCallback(evt => PlayerPrefs.SetInt("CursorRipples", evt.newValue ? 1 : 0));
|
|
_content.Add(rippleToggle);
|
|
|
|
_content.Add(CreateSliderWithInput("Sensitivity", 0.1f, 5.0f, PlayerPrefs.GetFloat("MouseSensitivity", 1.0f), val => PlayerPrefs.SetFloat("MouseSensitivity", val)));
|
|
|
|
var rawInputToggle = new Toggle("Raw Input (Bypass Acceleration)") { value = true };
|
|
_content.Add(rawInputToggle);
|
|
|
|
_mouseMetricsLabel = new Label("[(report: 0/sec latency: 0ms)]") { style = { fontSize = 11, color = Color.gray, marginTop = 5 } };
|
|
_content.Add(_mouseMetricsLabel);
|
|
}
|
|
#endregion
|
|
|
|
#region VIDEO TAB
|
|
private void RenderVideoTab()
|
|
{
|
|
_content.Add(CreateSection("RENDERER"));
|
|
var frameLimit = new DropdownField("Frame Limiter", new List<string> { "VSync", "Power Saving", "Optimal", "Unlimited" }, 2);
|
|
frameLimit.RegisterValueChangedCallback(evt => {
|
|
switch (evt.newValue) {
|
|
case "VSync": QualitySettings.vSyncCount = 1; Application.targetFrameRate = -1; break;
|
|
case "Power Saving": QualitySettings.vSyncCount = 0; Application.targetFrameRate = 60; break;
|
|
case "Optimal": QualitySettings.vSyncCount = 0; Application.targetFrameRate = 144; break;
|
|
case "Unlimited": QualitySettings.vSyncCount = 0; Application.targetFrameRate = 999; break;
|
|
}
|
|
});
|
|
_content.Add(frameLimit);
|
|
|
|
var fpsToggle = new Toggle("Show FPS Counter") { value = _fpsVisible };
|
|
fpsToggle.RegisterValueChangedCallback(evt => { _fpsVisible = evt.newValue; PerformanceOverlay.SetVisible(_fpsVisible); });
|
|
_content.Add(fpsToggle);
|
|
|
|
_content.Add(CreateSection("LAYOUT"));
|
|
Resolution native = Screen.currentResolution;
|
|
var resList = Screen.resolutions.Select(r => $"{r.width}x{r.height}").Distinct().Select(s => s == $"{native.width}x{native.height}" ? s + " (native)" : s).ToList();
|
|
var resDropdown = new DropdownField("Resolution", resList, resList.FindIndex(s => s.Contains("native")));
|
|
resDropdown.RegisterValueChangedCallback(evt => {
|
|
string[] parts = evt.newValue.Split(' ')[0].Split('x');
|
|
Screen.SetResolution(int.Parse(parts[0]), int.Parse(parts[1]), Screen.fullScreen);
|
|
});
|
|
_content.Add(resDropdown);
|
|
|
|
var fullToggle = new Toggle("Fullscreen Mode") { value = Screen.fullScreen };
|
|
fullToggle.RegisterValueChangedCallback(evt => Screen.fullScreen = evt.newValue);
|
|
_content.Add(fullToggle);
|
|
|
|
_content.Add(CreateSliderWithInput("Background Dim", 0, 100, PlayerPrefs.GetFloat("BackgroundDim", 50), val => PlayerPrefs.SetFloat("BackgroundDim", val)));
|
|
_content.Add(CreateSliderWithInput("UI Scale", 0.5f, 2.0f, PlayerPrefs.GetFloat("UIScale", 1.0f), val => uiManager.SetUIScale(val)));
|
|
}
|
|
#endregion
|
|
|
|
#region SOUND TAB
|
|
private void RenderSoundTab()
|
|
{
|
|
_content.Add(CreateSection("AUDIO VOLUMES"));
|
|
_content.Add(CreateAudioSlider("Master", "MasterVolume"));
|
|
_content.Add(CreateAudioSlider("Music", "MusicVolume"));
|
|
_content.Add(CreateAudioSlider("VFX", "VFXVolume"));
|
|
_content.Add(CreateAudioSlider("Player", "PlayerVolume"));
|
|
_content.Add(CreateAudioSlider("UI", "UIVolume"));
|
|
|
|
_content.Add(new Label("Use Scroll Wheel to control volume.") { style = { marginTop = 20, color = Color.gray, fontSize = 12 } });
|
|
}
|
|
|
|
private VisualElement CreateAudioSlider(string label, string prefKey)
|
|
{
|
|
var sliderRow = CreateSliderWithInput(label, 0, 100, PlayerPrefs.GetFloat(prefKey, 80), val => {
|
|
PlayerPrefs.SetFloat(prefKey, val);
|
|
AudioManager.Instance?.SetVolume(prefKey, val);
|
|
});
|
|
|
|
// Register wheel specifically on this row
|
|
sliderRow.RegisterCallback<WheelEvent>(evt => {
|
|
float current = PlayerPrefs.GetFloat(prefKey, 80f);
|
|
float newVal = Mathf.Clamp(current - (evt.delta.y * 2f), 0f, 100f);
|
|
PlayerPrefs.SetFloat(prefKey, newVal);
|
|
AudioManager.Instance?.SetVolume(prefKey, newVal);
|
|
|
|
// Visual update only (to avoid heavy re-render of whole list)
|
|
var slider = sliderRow.Q<Slider>();
|
|
if (slider != null) slider.value = newVal;
|
|
});
|
|
|
|
return sliderRow;
|
|
}
|
|
#endregion
|
|
|
|
#region CONTROL TAB
|
|
private void RenderControlTab()
|
|
{
|
|
_content.Add(CreateSection("KEY BINDINGS"));
|
|
var pendingLabel = new Label("Controls Implementation Pending context.");
|
|
pendingLabel.AddToClassList("text-body");
|
|
_content.Add(pendingLabel);
|
|
}
|
|
#endregion
|
|
|
|
private VisualElement CreateSection(string title)
|
|
{
|
|
var label = new Label(title);
|
|
label.AddToClassList("setting-section-header");
|
|
label.style.marginTop = 20;
|
|
return label;
|
|
}
|
|
|
|
private VisualElement CreateSliderWithInput(string labelText, float min, float max, float startVal, Action<float> OnValueChanged)
|
|
{
|
|
var row = new VisualElement { style = { flexDirection = FlexDirection.Row, alignItems = Align.Center, marginTop = 5, marginBottom = 5 } };
|
|
var label = new Label(labelText) { style = { width = Length.Percent(35) } };
|
|
label.AddToClassList("text-body");
|
|
|
|
var slider = new Slider(min, max) { value = startVal, style = { flexGrow = 1 } };
|
|
var input = new TextField { value = startVal.ToString("F1"), style = { width = 50, marginLeft = 10 } };
|
|
input.AddToClassList("input-field");
|
|
|
|
slider.RegisterCallback<PointerEnterEvent>(evt => {
|
|
_hoveredSlider = slider;
|
|
_hoveredOnChanged = OnValueChanged;
|
|
_sliderMin = min;
|
|
_sliderMax = max;
|
|
});
|
|
slider.RegisterCallback<PointerLeaveEvent>(evt => {
|
|
if (_hoveredSlider == slider) {
|
|
_hoveredSlider = null;
|
|
_hoveredOnChanged = null;
|
|
}
|
|
});
|
|
|
|
slider.RegisterValueChangedCallback(evt => {
|
|
float val = Mathf.Round(evt.newValue * 10f) / 10f;
|
|
if (input.panel?.focusController?.focusedElement != input.ElementAt(0)) input.value = val.ToString("F1");
|
|
OnValueChanged?.Invoke(val);
|
|
});
|
|
|
|
input.RegisterValueChangedCallback(evt => {
|
|
if (float.TryParse(evt.newValue, out float val)) {
|
|
slider.value = Mathf.Clamp(val, min, max);
|
|
OnValueChanged?.Invoke(slider.value);
|
|
}
|
|
});
|
|
|
|
row.Add(label);
|
|
row.Add(slider);
|
|
row.Add(input);
|
|
return row;
|
|
}
|
|
|
|
private void OnKeyDown(KeyDownEvent evt)
|
|
{
|
|
if (_hoveredSlider == null) return;
|
|
float step = (_sliderMax - _sliderMin) / 100f;
|
|
if (evt.keyCode == KeyCode.LeftArrow) _hoveredSlider.value -= step;
|
|
if (evt.keyCode == KeyCode.RightArrow) _hoveredSlider.value += step;
|
|
}
|
|
|
|
public override void Update()
|
|
{
|
|
if (_activeTab == "GENERAL" && _mouseMetricsLabel != null)
|
|
{
|
|
var (polling, latency) = MouseMetricsHelper.GetMetrics();
|
|
_mouseMetricsLabel.text = $"[(report: {polling}/sec latency: {latency:F0}ms)]";
|
|
}
|
|
}
|
|
|
|
public override async Task PlayTransitionIn()
|
|
{
|
|
root.style.display = DisplayStyle.Flex;
|
|
_sidebar.style.translate = new StyleTranslate(new Translate(Length.Percent(-100), 0));
|
|
await Tween.Custom(-100f, 0f, duration: 0.4f, ease: Ease.OutQuad, onValueChange: val => _sidebar.style.translate = new StyleTranslate(new Translate(Length.Percent(val), 0)));
|
|
}
|
|
|
|
public override async Task PlayTransitionOut()
|
|
{
|
|
await Tween.Custom(0f, -100f, duration: 0.3f, ease: Ease.InQuad, onValueChange: val => _sidebar.style.translate = new StyleTranslate(new Translate(Length.Percent(val), 0)));
|
|
Hide();
|
|
}
|
|
}
|
|
}
|