From 0f31db374acf65643e9d2189e70ac0f1a6860f8a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 20 Mar 2026 01:35:39 +0000
Subject: [PATCH 1/6] Implement slider translation curve presets and custom
curve settings
Co-authored-by: PalmarHealer <93807726+PalmarHealer@users.noreply.github.com>
---
ControlPad/EventHandler.cs | 2 +-
ControlPad/Settings.cs | 72 +++++++++++++++++-
ControlPad/SliderTranslationCurve.cs | 76 +++++++++++++++++++
.../UI Elements/SettingsUserControl.xaml | 69 +++++++++++++----
.../UI Elements/SettingsUserControl.xaml.cs | 46 +++++++++--
5 files changed, 244 insertions(+), 21 deletions(-)
create mode 100644 ControlPad/SliderTranslationCurve.cs
diff --git a/ControlPad/EventHandler.cs b/ControlPad/EventHandler.cs
index 18fc471..4cc74b9 100644
--- a/ControlPad/EventHandler.cs
+++ b/ControlPad/EventHandler.cs
@@ -189,7 +189,7 @@ private float SliderToFloat(int value, int mode = 0)
if (normalized < 0.005f)
return 0f;
- return (float)Math.Pow(normalized, Settings.TranslationExponent);
+ return SliderTranslationCurve.Apply(normalized);
}
}
}
diff --git a/ControlPad/Settings.cs b/ControlPad/Settings.cs
index c1d97dd..46f3005 100644
--- a/ControlPad/Settings.cs
+++ b/ControlPad/Settings.cs
@@ -15,6 +15,11 @@ public static class Settings
private static bool _startMinimized = false;
private static bool _minimizeToSystemTray = true;
private static double _translationExponent = 1d;
+ private static string _translationCurvePreset = "linear";
+ private static double _translationCurveX1 = 0d;
+ private static double _translationCurveY1 = 0d;
+ private static double _translationCurveX2 = 1d;
+ private static double _translationCurveY2 = 1d;
private static int _selectedThemeIndex = 0;
private static int _selectedBackgroundIndex = 3;
private static int _sliderDeadZone = 4;
@@ -115,6 +120,56 @@ public static double TranslationExponent
}
}
+ public static string TranslationCurvePreset
+ {
+ get => _translationCurvePreset;
+ set
+ {
+ _translationCurvePreset = SliderTranslationCurve.IsSupportedPreset(value) ? value : "linear";
+ Save();
+ }
+ }
+
+ public static double TranslationCurveX1
+ {
+ get => _translationCurveX1;
+ set
+ {
+ _translationCurveX1 = Math.Clamp(value, 0d, 1d);
+ Save();
+ }
+ }
+
+ public static double TranslationCurveY1
+ {
+ get => _translationCurveY1;
+ set
+ {
+ _translationCurveY1 = Math.Clamp(value, 0d, 1d);
+ Save();
+ }
+ }
+
+ public static double TranslationCurveX2
+ {
+ get => _translationCurveX2;
+ set
+ {
+ _translationCurveX2 = Math.Clamp(value, 0d, 1d);
+ Save();
+ }
+ }
+
+ public static double TranslationCurveY2
+ {
+ get => _translationCurveY2;
+ set
+ {
+ _translationCurveY2 = Math.Clamp(value, 0d, 1d);
+ Save();
+ }
+ }
+
private class Data
{
public bool TrayIconMessageShown { get; set; } = false;
@@ -122,6 +177,11 @@ private class Data
public bool StartMinimized { get; set; } = true;
public bool MinimizeToSystemTray { get; set; } = true;
public double TranslationExponent { get; set; } = 1d;
+ public string TranslationCurvePreset { get; set; } = "linear";
+ public double TranslationCurveX1 { get; set; } = 0d;
+ public double TranslationCurveY1 { get; set; } = 0d;
+ public double TranslationCurveX2 { get; set; } = 1d;
+ public double TranslationCurveY2 { get; set; } = 1d;
public int SelectedThemeIndex { get; set; } = 0;
public int SelectedBackgroundIndex { get; set; } = 3;
public int SliderDeadZone { get; set; } = 4;
@@ -148,6 +208,11 @@ public static void Load()
_selectedBackgroundIndex = data.SelectedBackgroundIndex;
_sliderDeadZone = data.SliderDeadZone;
_translationExponent = data.TranslationExponent;
+ _translationCurvePreset = SliderTranslationCurve.IsSupportedPreset(data.TranslationCurvePreset) ? data.TranslationCurvePreset : "linear";
+ _translationCurveX1 = Math.Clamp(data.TranslationCurveX1, 0d, 1d);
+ _translationCurveY1 = Math.Clamp(data.TranslationCurveY1, 0d, 1d);
+ _translationCurveX2 = Math.Clamp(data.TranslationCurveX2, 0d, 1d);
+ _translationCurveY2 = Math.Clamp(data.TranslationCurveY2, 0d, 1d);
_unmuteOnSliderChange = data.UnmuteOnSliderChange;
}
catch
@@ -170,6 +235,11 @@ private static void Save()
SelectedBackgroundIndex = _selectedBackgroundIndex,
SliderDeadZone = _sliderDeadZone,
TranslationExponent = _translationExponent,
+ TranslationCurvePreset = _translationCurvePreset,
+ TranslationCurveX1 = _translationCurveX1,
+ TranslationCurveY1 = _translationCurveY1,
+ TranslationCurveX2 = _translationCurveX2,
+ TranslationCurveY2 = _translationCurveY2,
UnmuteOnSliderChange = _unmuteOnSliderChange,
};
@@ -183,4 +253,4 @@ private static void Save()
}
}
}
-}
\ No newline at end of file
+}
diff --git a/ControlPad/SliderTranslationCurve.cs b/ControlPad/SliderTranslationCurve.cs
new file mode 100644
index 0000000..c69a569
--- /dev/null
+++ b/ControlPad/SliderTranslationCurve.cs
@@ -0,0 +1,76 @@
+namespace ControlPad
+{
+ public static class SliderTranslationCurve
+ {
+ private const double Epsilon = 1e-6;
+
+ public static bool IsSupportedPreset(string? preset)
+ {
+ return preset is "ease" or "linear" or "ease-in" or "ease-out" or "ease-in-out" or "custom";
+ }
+
+ public static (double x1, double y1, double x2, double y2) GetPresetControlPoints(string preset)
+ {
+ return preset switch
+ {
+ "ease" => (0.25d, 0.1d, 0.25d, 1d),
+ "linear" => (0d, 0d, 1d, 1d),
+ "ease-in" => (0.42d, 0d, 1d, 1d),
+ "ease-out" => (0d, 0d, 0.58d, 1d),
+ "ease-in-out" => (0.42d, 0d, 0.58d, 1d),
+ _ => (0d, 0d, 1d, 1d),
+ };
+ }
+
+ public static float Apply(float input)
+ {
+ double t = Math.Clamp(input, 0f, 1f);
+ var controlPoints = Settings.TranslationCurvePreset == "custom"
+ ? (Settings.TranslationCurveX1, Settings.TranslationCurveY1, Settings.TranslationCurveX2, Settings.TranslationCurveY2)
+ : GetPresetControlPoints(Settings.TranslationCurvePreset);
+
+ return (float)Evaluate(controlPoints.Item1, controlPoints.Item2, controlPoints.Item3, controlPoints.Item4, t);
+ }
+
+ private static double Evaluate(double x1, double y1, double x2, double y2, double xTarget)
+ {
+ x1 = Math.Clamp(x1, 0d, 1d);
+ x2 = Math.Clamp(x2, 0d, 1d);
+ y1 = Math.Clamp(y1, 0d, 1d);
+ y2 = Math.Clamp(y2, 0d, 1d);
+ xTarget = Math.Clamp(xTarget, 0d, 1d);
+
+ if (xTarget <= 0d)
+ return 0d;
+ if (xTarget >= 1d)
+ return 1d;
+
+ double lower = 0d;
+ double upper = 1d;
+ double t = xTarget;
+
+ for (int i = 0; i < 20; i++)
+ {
+ t = (lower + upper) * 0.5d;
+ double x = Bezier(t, 0d, x1, x2, 1d);
+ if (Math.Abs(x - xTarget) < Epsilon)
+ break;
+ if (x < xTarget)
+ lower = t;
+ else
+ upper = t;
+ }
+
+ return Bezier(t, 0d, y1, y2, 1d);
+ }
+
+ private static double Bezier(double t, double p0, double p1, double p2, double p3)
+ {
+ double oneMinusT = 1d - t;
+ return oneMinusT * oneMinusT * oneMinusT * p0
+ + 3d * oneMinusT * oneMinusT * t * p1
+ + 3d * oneMinusT * t * t * p2
+ + t * t * t * p3;
+ }
+ }
+}
diff --git a/ControlPad/UI Elements/SettingsUserControl.xaml b/ControlPad/UI Elements/SettingsUserControl.xaml
index 234baf6..d40e4f3 100644
--- a/ControlPad/UI Elements/SettingsUserControl.xaml
+++ b/ControlPad/UI Elements/SettingsUserControl.xaml
@@ -128,7 +128,7 @@
Unchecked="cb_UnmuteOnSliderChange_Checked"/>
-
+
@@ -136,23 +136,66 @@
-
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ControlPad/UI Elements/SettingsUserControl.xaml.cs b/ControlPad/UI Elements/SettingsUserControl.xaml.cs
index dfae4c5..aabe14f 100644
--- a/ControlPad/UI Elements/SettingsUserControl.xaml.cs
+++ b/ControlPad/UI Elements/SettingsUserControl.xaml.cs
@@ -20,6 +20,7 @@ public partial class SettingsUserControl : UserControl
{
private readonly bool _isInitialized = false;
private readonly MainWindow _mainWindow;
+ private static readonly string[] TranslationCurvePresets = { "ease", "linear", "ease-in", "ease-out", "ease-in-out", "custom" };
public SettingsUserControl(MainWindow mainWindow)
{
@@ -88,15 +89,36 @@ private void BackgroundComboBox_SelectionChanged(object sender, SelectionChanged
Settings.SelectedBackgroundIndex = BackgroundComboBox.SelectedIndex;
}
- private void nb_TranslationExponent_ValueChanged(object sender, Wpf.Ui.Controls.NumberBoxValueChangedEventArgs e)
+ private void TranslationCurvePresetComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!_isInitialized)
return;
- if (nb_TranslationExponent.Value != null)
- Settings.TranslationExponent = (double)nb_TranslationExponent.Value;
- else
- Settings.TranslationExponent = 1.0d;
+ string preset = TranslationCurvePresets[Math.Clamp(TranslationCurvePresetComboBox.SelectedIndex, 0, TranslationCurvePresets.Length - 1)];
+ Settings.TranslationCurvePreset = preset;
+
+ if (preset != "custom")
+ {
+ var cp = SliderTranslationCurve.GetPresetControlPoints(preset);
+ Settings.TranslationCurveX1 = cp.x1;
+ Settings.TranslationCurveY1 = cp.y1;
+ Settings.TranslationCurveX2 = cp.x2;
+ Settings.TranslationCurveY2 = cp.y2;
+ SetCustomCurveControls(cp.x1, cp.y1, cp.x2, cp.y2);
+ }
+
+ CustomCurveGrid.IsEnabled = preset == "custom";
+ }
+
+ private void nb_CustomCurve_ValueChanged(object sender, Wpf.Ui.Controls.NumberBoxValueChangedEventArgs e)
+ {
+ if (!_isInitialized || Settings.TranslationCurvePreset != "custom")
+ return;
+
+ Settings.TranslationCurveX1 = nb_CurveX1.Value ?? 0d;
+ Settings.TranslationCurveY1 = nb_CurveY1.Value ?? 0d;
+ Settings.TranslationCurveX2 = nb_CurveX2.Value ?? 1d;
+ Settings.TranslationCurveY2 = nb_CurveY2.Value ?? 1d;
}
public static void ChangeAppTheme(int index)
@@ -145,7 +167,19 @@ public void SetControls()
cb_UnmuteOnSliderChange.IsChecked = Settings.UnmuteOnSliderChange;
ThemeComboBox.SelectedIndex = Settings.SelectedThemeIndex;
BackgroundComboBox.SelectedIndex = Settings.SelectedBackgroundIndex;
- nb_TranslationExponent.Value = Settings.TranslationExponent;
+
+ int presetIndex = Array.IndexOf(TranslationCurvePresets, Settings.TranslationCurvePreset);
+ TranslationCurvePresetComboBox.SelectedIndex = presetIndex >= 0 ? presetIndex : 1;
+ SetCustomCurveControls(Settings.TranslationCurveX1, Settings.TranslationCurveY1, Settings.TranslationCurveX2, Settings.TranslationCurveY2);
+ CustomCurveGrid.IsEnabled = Settings.TranslationCurvePreset == "custom";
+ }
+
+ private void SetCustomCurveControls(double x1, double y1, double x2, double y2)
+ {
+ nb_CurveX1.Value = x1;
+ nb_CurveY1.Value = y1;
+ nb_CurveX2.Value = x2;
+ nb_CurveY2.Value = y2;
}
private void Btn_Presets_Click(object sender, RoutedEventArgs e)
From cf984cdd3079668bfe697984b154985982036768 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 20 Mar 2026 01:36:35 +0000
Subject: [PATCH 2/6] Optimize curve mapping and suppress redundant UI events
Co-authored-by: PalmarHealer <93807726+PalmarHealer@users.noreply.github.com>
---
ControlPad/SliderTranslationCurve.cs | 36 ++++++++++++++++---
.../UI Elements/SettingsUserControl.xaml.cs | 19 +++++++---
2 files changed, 46 insertions(+), 9 deletions(-)
diff --git a/ControlPad/SliderTranslationCurve.cs b/ControlPad/SliderTranslationCurve.cs
index c69a569..700f2ec 100644
--- a/ControlPad/SliderTranslationCurve.cs
+++ b/ControlPad/SliderTranslationCurve.cs
@@ -3,6 +3,13 @@ namespace ControlPad
public static class SliderTranslationCurve
{
private const double Epsilon = 1e-6;
+ private const int MaxBisectionIterations = 20;
+ private static string? _lastPreset;
+ private static double _lastX1;
+ private static double _lastY1;
+ private static double _lastX2;
+ private static double _lastY2;
+ private static (double x1, double y1, double x2, double y2) _cachedControlPoints = (0d, 0d, 1d, 1d);
public static bool IsSupportedPreset(string? preset)
{
@@ -25,13 +32,34 @@ public static (double x1, double y1, double x2, double y2) GetPresetControlPoint
public static float Apply(float input)
{
double t = Math.Clamp(input, 0f, 1f);
- var controlPoints = Settings.TranslationCurvePreset == "custom"
- ? (Settings.TranslationCurveX1, Settings.TranslationCurveY1, Settings.TranslationCurveX2, Settings.TranslationCurveY2)
- : GetPresetControlPoints(Settings.TranslationCurvePreset);
+ var controlPoints = GetControlPoints();
return (float)Evaluate(controlPoints.Item1, controlPoints.Item2, controlPoints.Item3, controlPoints.Item4, t);
}
+ private static (double x1, double y1, double x2, double y2) GetControlPoints()
+ {
+ string preset = Settings.TranslationCurvePreset;
+ double x1 = Settings.TranslationCurveX1;
+ double y1 = Settings.TranslationCurveY1;
+ double x2 = Settings.TranslationCurveX2;
+ double y2 = Settings.TranslationCurveY2;
+
+ if (_lastPreset == preset && _lastX1 == x1 && _lastY1 == y1 && _lastX2 == x2 && _lastY2 == y2)
+ return _cachedControlPoints;
+
+ _cachedControlPoints = preset == "custom"
+ ? (x1, y1, x2, y2)
+ : GetPresetControlPoints(preset);
+
+ _lastPreset = preset;
+ _lastX1 = x1;
+ _lastY1 = y1;
+ _lastX2 = x2;
+ _lastY2 = y2;
+ return _cachedControlPoints;
+ }
+
private static double Evaluate(double x1, double y1, double x2, double y2, double xTarget)
{
x1 = Math.Clamp(x1, 0d, 1d);
@@ -49,7 +77,7 @@ private static double Evaluate(double x1, double y1, double x2, double y2, doubl
double upper = 1d;
double t = xTarget;
- for (int i = 0; i < 20; i++)
+ for (int i = 0; i < MaxBisectionIterations; i++)
{
t = (lower + upper) * 0.5d;
double x = Bezier(t, 0d, x1, x2, 1d);
diff --git a/ControlPad/UI Elements/SettingsUserControl.xaml.cs b/ControlPad/UI Elements/SettingsUserControl.xaml.cs
index aabe14f..18632c9 100644
--- a/ControlPad/UI Elements/SettingsUserControl.xaml.cs
+++ b/ControlPad/UI Elements/SettingsUserControl.xaml.cs
@@ -21,6 +21,7 @@ public partial class SettingsUserControl : UserControl
private readonly bool _isInitialized = false;
private readonly MainWindow _mainWindow;
private static readonly string[] TranslationCurvePresets = { "ease", "linear", "ease-in", "ease-out", "ease-in-out", "custom" };
+ private bool _suppressCustomCurveEvents = false;
public SettingsUserControl(MainWindow mainWindow)
{
@@ -112,7 +113,7 @@ private void TranslationCurvePresetComboBox_SelectionChanged(object sender, Sele
private void nb_CustomCurve_ValueChanged(object sender, Wpf.Ui.Controls.NumberBoxValueChangedEventArgs e)
{
- if (!_isInitialized || Settings.TranslationCurvePreset != "custom")
+ if (!_isInitialized || _suppressCustomCurveEvents || Settings.TranslationCurvePreset != "custom")
return;
Settings.TranslationCurveX1 = nb_CurveX1.Value ?? 0d;
@@ -176,10 +177,18 @@ public void SetControls()
private void SetCustomCurveControls(double x1, double y1, double x2, double y2)
{
- nb_CurveX1.Value = x1;
- nb_CurveY1.Value = y1;
- nb_CurveX2.Value = x2;
- nb_CurveY2.Value = y2;
+ _suppressCustomCurveEvents = true;
+ try
+ {
+ nb_CurveX1.Value = x1;
+ nb_CurveY1.Value = y1;
+ nb_CurveX2.Value = x2;
+ nb_CurveY2.Value = y2;
+ }
+ finally
+ {
+ _suppressCustomCurveEvents = false;
+ }
}
private void Btn_Presets_Click(object sender, RoutedEventArgs e)
From 40394631e880ab3a1f9c7450b0862560269c9a8d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 20 Mar 2026 02:48:33 +0000
Subject: [PATCH 3/6] Add scrolling overflow to settings panel
Co-authored-by: PalmarHealer <93807726+PalmarHealer@users.noreply.github.com>
---
ControlPad/UI Elements/SettingsUserControl.xaml | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/ControlPad/UI Elements/SettingsUserControl.xaml b/ControlPad/UI Elements/SettingsUserControl.xaml
index d40e4f3..809aad1 100644
--- a/ControlPad/UI Elements/SettingsUserControl.xaml
+++ b/ControlPad/UI Elements/SettingsUserControl.xaml
@@ -10,7 +10,8 @@
>
-
+
@@ -275,6 +276,6 @@
-
+
From 954c38d43475817a81783988fd9125dd1049d831 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 20 Mar 2026 02:49:23 +0000
Subject: [PATCH 4/6] Preserve settings layout while enabling scroll overflow
Co-authored-by: PalmarHealer <93807726+PalmarHealer@users.noreply.github.com>
---
ControlPad/UI Elements/SettingsUserControl.xaml | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/ControlPad/UI Elements/SettingsUserControl.xaml b/ControlPad/UI Elements/SettingsUserControl.xaml
index 809aad1..5c00be6 100644
--- a/ControlPad/UI Elements/SettingsUserControl.xaml
+++ b/ControlPad/UI Elements/SettingsUserControl.xaml
@@ -12,7 +12,8 @@
-
+
+
@@ -275,7 +276,8 @@
-
+
+
From 704661bfc96aa3b4630a3c93c514e6f41a25e465 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 20 Mar 2026 16:00:43 +0000
Subject: [PATCH 5/6] Revert "Preserve settings layout while enabling scroll
overflow"
This reverts commit 954c38d43475817a81783988fd9125dd1049d831.
---
ControlPad/UI Elements/SettingsUserControl.xaml | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/ControlPad/UI Elements/SettingsUserControl.xaml b/ControlPad/UI Elements/SettingsUserControl.xaml
index 5c00be6..809aad1 100644
--- a/ControlPad/UI Elements/SettingsUserControl.xaml
+++ b/ControlPad/UI Elements/SettingsUserControl.xaml
@@ -12,8 +12,7 @@
-
-
+
@@ -276,8 +275,7 @@
-
-
+
From 2571daa3e540ba026b27c9efb2aa171bf916b93d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 20 Mar 2026 16:00:43 +0000
Subject: [PATCH 6/6] Revert "Add scrolling overflow to settings panel"
This reverts commit 40394631e880ab3a1f9c7450b0862560269c9a8d.
---
ControlPad/UI Elements/SettingsUserControl.xaml | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/ControlPad/UI Elements/SettingsUserControl.xaml b/ControlPad/UI Elements/SettingsUserControl.xaml
index 809aad1..d40e4f3 100644
--- a/ControlPad/UI Elements/SettingsUserControl.xaml
+++ b/ControlPad/UI Elements/SettingsUserControl.xaml
@@ -10,8 +10,7 @@
>
-
+
@@ -276,6 +275,6 @@
-
+