From 85f36dd3e7baf0c835157f84e5056cef93405060 Mon Sep 17 00:00:00 2001 From: Max54nj Date: Tue, 28 Oct 2025 22:57:27 +0100 Subject: [PATCH] feat: add alternative input field to slider setting --- SkEditor/API/Settings/Types/SliderSetting.cs | 136 +++++++++++++++---- 1 file changed, 109 insertions(+), 27 deletions(-) diff --git a/SkEditor/API/Settings/Types/SliderSetting.cs b/SkEditor/API/Settings/Types/SliderSetting.cs index 66b0badc..a54480c5 100644 --- a/SkEditor/API/Settings/Types/SliderSetting.cs +++ b/SkEditor/API/Settings/Types/SliderSetting.cs @@ -1,6 +1,8 @@ using System; using System.Timers; using Avalonia.Controls; +using Avalonia.Layout; +using FluentAvalonia.UI.Controls; using Newtonsoft.Json.Linq; namespace SkEditor.API.Settings.Types; @@ -8,14 +10,22 @@ namespace SkEditor.API.Settings.Types; /// /// Represent a slider setting. /// -public class SliderSetting(double min = 0.0, double max = 100.0, double tickFrequency = 1.0, bool isSnapToTickEnabled = true, bool debounce = true) : ISettingType +public class SliderSetting( + double min = 0.0, + double max = 100.0, + double tickFrequency = 1.0, + bool showAlternativeInput = true, + bool isSnapToTickEnabled = true, + bool debounce = true + ) : ISettingType { public double Min { get; } = min; public double Max { get; } = max; public double TickFrequency { get; } = tickFrequency; public bool IsSnapToTickEnabled { get; } = isSnapToTickEnabled; public bool Debounce { get; } = debounce; - + public bool ShowAlternativeInput { get; } = showAlternativeInput; + public object Deserialize(JToken value) { return value.Value(); @@ -28,43 +38,115 @@ public JToken Serialize(object value) public Control CreateControl(object raw, Action onChanged) { - double value = (double)raw; - Slider slider = new() + double initial = (double)raw; + + var slider = CreateSlider(initial); + NumberBox? numberBox = ShowAlternativeInput ? CreateNumberBox(initial) : null; + + if (Debounce) + { + SetupDebouncedSlider(slider, numberBox, onChanged); + } + else { - Minimum = Min, - Maximum = Max, + SetupImmediateSlider(slider, numberBox, onChanged); + } + + if (ShowAlternativeInput && numberBox is not null) + { + SetupNumberBox(numberBox, slider, onChanged); + + var panel = new StackPanel + { + Orientation = Orientation.Horizontal, + Spacing = 5 + }; + + panel.Children.Add(numberBox); + panel.Children.Add(slider); + return panel; + } + + return slider; + } + + private Slider CreateSlider(double value) + { + return new Slider + { + Minimum = Min, + Maximum = Max, Value = value, TickFrequency = TickFrequency, IsSnapToTickEnabled = IsSnapToTickEnabled, Width = 150, MinWidth = 50, }; - - if (Debounce) + } + + private NumberBox CreateNumberBox(double value) + { + return new NumberBox + { + Minimum = Min, + Maximum = Max, + Value = value, + VerticalAlignment = VerticalAlignment.Center, + }; + } + + private static void SetupImmediateSlider(Slider slider, NumberBox? numberBox, Action onChanged) + { + slider.ValueChanged += (_, _) => + { + onChanged(slider.Value); + if (numberBox is not null) + { + numberBox.Value = slider.Value; + } + }; + } + + private static void SetupDebouncedSlider(Slider slider, NumberBox? numberBox, Action onChanged) + { + var debounceTimer = new Timer(300) { AutoReset = false }; + double latestValue = slider.Value; + + debounceTimer.Elapsed += (_, _) => { - Timer? debounceTimer = null; - slider.ValueChanged += (_, args) => + Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() => { - debounceTimer?.Stop(); - debounceTimer = new Timer(300); - debounceTimer.Elapsed += (_, _) => + onChanged(latestValue); + if (numberBox is not null) { - debounceTimer.Stop(); - Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() => onChanged(slider.Value)); - }; - debounceTimer.AutoReset = false; - debounceTimer.Start(); - }; - } - else + numberBox.Value = slider.Value; + } + }); + }; + + slider.ValueChanged += (_, _) => + { + latestValue = slider.Value; + debounceTimer.Stop(); + debounceTimer.Start(); + }; + } + + private static void SetupNumberBox(NumberBox numberBox, Slider slider, Action onChanged) + { + numberBox.ValueChanged += (_, _) => { - slider.ValueChanged += (_, _) => + var newValue = numberBox.Value; + + if (Double.IsNaN(newValue)) { - onChanged(slider.Value); - }; - } - - return slider; + numberBox.Value = slider.Value; + return; + } + + onChanged(newValue); + slider.Value = newValue; + }; } public bool IsSelfManaged => false;