diff --git a/MapUiTools/WPF/MapUiTools.WPF.csproj b/MapUiTools/WPF/MapUiTools.WPF.csproj index d0e590f..b77ab69 100644 --- a/MapUiTools/WPF/MapUiTools.WPF.csproj +++ b/MapUiTools/WPF/MapUiTools.WPF.csproj @@ -10,6 +10,7 @@ Copyright © 2023 Clemens Fischer true x64 + false diff --git a/PhotoLocator/Helpers/ColorToneControl.xaml b/PhotoLocator/Controls/ColorToneControl.xaml similarity index 84% rename from PhotoLocator/Helpers/ColorToneControl.xaml rename to PhotoLocator/Controls/ColorToneControl.xaml index 0be2700..eb4974e 100644 --- a/PhotoLocator/Helpers/ColorToneControl.xaml +++ b/PhotoLocator/Controls/ColorToneControl.xaml @@ -1,10 +1,10 @@ - diff --git a/PhotoLocator/Helpers/ColorToneControl.xaml.cs b/PhotoLocator/Controls/ColorToneControl.xaml.cs similarity index 99% rename from PhotoLocator/Helpers/ColorToneControl.xaml.cs rename to PhotoLocator/Controls/ColorToneControl.xaml.cs index a3c7897..d69e50b 100644 --- a/PhotoLocator/Helpers/ColorToneControl.xaml.cs +++ b/PhotoLocator/Controls/ColorToneControl.xaml.cs @@ -1,4 +1,5 @@ using PhotoLocator.BitmapOperations; +using PhotoLocator.Helpers; using System; using System.ComponentModel; using System.Threading.Tasks; @@ -8,7 +9,7 @@ using System.Windows.Media; using System.Windows.Media.Imaging; -namespace PhotoLocator.Helpers +namespace PhotoLocator.Controls { /// /// Interaction logic for ColorToneControl.xaml diff --git a/PhotoLocator/Helpers/CropControl.xaml b/PhotoLocator/Controls/CropControl.xaml similarity index 97% rename from PhotoLocator/Helpers/CropControl.xaml rename to PhotoLocator/Controls/CropControl.xaml index fd85a30..4d34aaa 100644 --- a/PhotoLocator/Helpers/CropControl.xaml +++ b/PhotoLocator/Controls/CropControl.xaml @@ -1,9 +1,9 @@ - + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PhotoLocator/Controls/DropDownSlider.xaml.cs b/PhotoLocator/Controls/DropDownSlider.xaml.cs new file mode 100644 index 0000000..f749329 --- /dev/null +++ b/PhotoLocator/Controls/DropDownSlider.xaml.cs @@ -0,0 +1,87 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Controls; + +namespace PhotoLocator.Controls; + +public partial class DropDownSlider : UserControl +{ + bool _onTextChangedRunning, _onNumericValueChangedRunning; + + public DropDownSlider() + { + InitializeComponent(); + } + + public static readonly DependencyProperty TextProperty = DependencyProperty.Register( + nameof(Text), typeof(string), typeof(DropDownSlider), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnTextChanged)); + + public string Text + { + get => (string)GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is not DropDownSlider control || control._onNumericValueChangedRunning) + return; + if (double.TryParse(control.Text, NumberStyles.Float, CultureInfo.InvariantCulture, out var v)) + { + control._onTextChangedRunning = true; + control.NumericValue = Math.Clamp(v, control.Minimum, control.Maximum); + control._onTextChangedRunning = false; + } + } + + public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register( + nameof(Minimum), typeof(double), typeof(DropDownSlider), new PropertyMetadata(0.0)); + public double Minimum { get => (double)GetValue(MinimumProperty); set => SetValue(MinimumProperty, value); } + + public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register( + nameof(Maximum), typeof(double), typeof(DropDownSlider), new PropertyMetadata(100.0)); + public double Maximum { get => (double)GetValue(MaximumProperty); set => SetValue(MaximumProperty, value); } + + public static readonly DependencyProperty TickFrequencyProperty = DependencyProperty.Register( + nameof(TickFrequency), typeof(double), typeof(DropDownSlider), new PropertyMetadata(1.0)); + public double TickFrequency { get => (double)GetValue(TickFrequencyProperty); set => SetValue(TickFrequencyProperty, value); } + + public static readonly DependencyProperty NumericValueProperty = DependencyProperty.Register( + nameof(NumericValue), typeof(double), typeof(DropDownSlider), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnNumericValueChanged)); + public double NumericValue { get => (double)GetValue(NumericValueProperty); set => SetValue(NumericValueProperty, value); } + + public static readonly DependencyProperty DecimalsProperty = DependencyProperty.Register( + nameof(Decimals), typeof(int), typeof(DropDownSlider), new PropertyMetadata(1, OnFormatChanged)); + public int Decimals { get => (int)GetValue(DecimalsProperty); set => SetValue(DecimalsProperty, value); } + + public static readonly DependencyProperty SliderValueUnitsProperty = DependencyProperty.Register( + nameof(SliderValueUnits), typeof(string), typeof(DropDownSlider), new PropertyMetadata(string.Empty, OnFormatChanged)); + public string SliderValueUnits { get => (string)GetValue(SliderValueUnitsProperty); set => SetValue(SliderValueUnitsProperty, value); } + + static void OnFormatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is DropDownSlider control) + control.UpdateFormattedValue(); + } + + public static readonly DependencyProperty FormattedNumericValueProperty = DependencyProperty.Register( + nameof(FormattedNumericValue), typeof(string), typeof(DropDownSlider), new PropertyMetadata(string.Empty)); + public string FormattedNumericValue { get => (string)GetValue(FormattedNumericValueProperty); private set => SetValue(FormattedNumericValueProperty, value); } + + static void OnNumericValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is not DropDownSlider control) + return; + control._onNumericValueChangedRunning = true; + if (!control._onTextChangedRunning) + control.Text = control.NumericValue.ToString("F" + control.Decimals.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + control.UpdateFormattedValue(); + control._onNumericValueChangedRunning = false; + } + + void UpdateFormattedValue() + { + FormattedNumericValue = NumericValue.ToString("F" + Decimals.ToString(CultureInfo.InvariantCulture), CultureInfo.CurrentCulture) + SliderValueUnits; + } +} diff --git a/PhotoLocator/Helpers/IntMath.cs b/PhotoLocator/Helpers/IntMath.cs index 89e0186..c53b1d3 100644 --- a/PhotoLocator/Helpers/IntMath.cs +++ b/PhotoLocator/Helpers/IntMath.cs @@ -17,6 +17,9 @@ public static int Round(double a) return (int)Math.Round(a); } + /// + /// Clamp without min/max check, for better performance when the caller can guarantee the value is within range. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Clamp(int value, int min, int max) { diff --git a/PhotoLocator/Helpers/RealMath.cs b/PhotoLocator/Helpers/RealMath.cs index b5bec1e..83bd83d 100644 --- a/PhotoLocator/Helpers/RealMath.cs +++ b/PhotoLocator/Helpers/RealMath.cs @@ -5,6 +5,9 @@ namespace PhotoLocator.Helpers { public static class RealMath { + /// + /// Clamp without min/max sanity check, for better performance when the caller can guarantee that max>min + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Clamp(float value, float min, float max) { @@ -15,6 +18,9 @@ public static float Clamp(float value, float min, float max) return value; } + /// + /// Clamp without min/max sanity check, for better performance when the caller can guarantee that max>min + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Clamp(double value, double min, double max) { @@ -53,7 +59,6 @@ public static float SmoothStep(float x) return (3.0f - 2.0f * x) * x * x; } - /// /// Smooth step edge between min and max /// diff --git a/PhotoLocator/Helpers/StringExtensions.cs b/PhotoLocator/Helpers/StringExtensions.cs index fcf4607..80e393d 100644 --- a/PhotoLocator/Helpers/StringExtensions.cs +++ b/PhotoLocator/Helpers/StringExtensions.cs @@ -6,5 +6,10 @@ public static string TrimPath(this string path) { return path.Trim(' ', '"'); } + + public static string TrimInvariantValue(this string str) + { + return str.Trim().Replace(',', '.'); + } } } diff --git a/PhotoLocator/LocalContrastView.xaml b/PhotoLocator/LocalContrastView.xaml index 136b21f..4c958ef 100644 --- a/PhotoLocator/LocalContrastView.xaml +++ b/PhotoLocator/LocalContrastView.xaml @@ -4,7 +4,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:sys="clr-namespace:System;assembly=mscorlib" - xmlns:local="clr-namespace:PhotoLocator" xmlns:helpers="clr-namespace:PhotoLocator.Helpers" + xmlns:local="clr-namespace:PhotoLocator" xmlns:controls="clr-namespace:PhotoLocator.Controls" mc:Ignorable="d" d:DataContext="{d:DesignInstance Type=local:LocalContrastViewModel, IsDesignTimeCreatable=True}" Background="Black" @@ -181,7 +181,7 @@ - +