using System; using System.Collections.Generic; using System.Windows; namespace NebulaAuth.Theme; public partial class FontScaleWindow : Window { private readonly Func _diagonal = (h, w) => Math.Sqrt(h * h + w * w); private double _currentDiagonal; private double _defaultDiagonal = 1; private readonly HashSet _cachedObjects = new(); private bool _loaded = false; public FontScaleWindow() { Loaded += OnLoaded; } private void OnLoaded(object sender, RoutedEventArgs e) { var w = (FontScaleWindow)sender; w._defaultDiagonal = _diagonal(MinHeight, MinWidth); w.SizeChanged += OnSizeChanged; w.Loaded -= OnLoaded; _loaded = true; w.Width += 1; w.Width -= 1; } //<-------------------Window--------------------> public double DefaultFontSize { get => (double)GetValue(DefaultFontSizeProperty); set => SetValue(DefaultFontSizeProperty, value); } public double ScaleCoefficient { get => (double)GetValue(ScaleCoefficientProperty); set => SetValue(ScaleCoefficientProperty, value); } // Using a DependencyProperty as the backing store for ScaleCoefficient. This enables animation, styling, binding, etc... public static readonly DependencyProperty ScaleCoefficientProperty = DependencyProperty.Register("ScaleCoefficient", typeof(double), typeof(FontScaleWindow), new PropertyMetadata(1d)); public static readonly DependencyProperty DefaultFontSizeProperty = DependencyProperty.Register("DefaultFontSize", typeof(double), typeof(FontScaleWindow), new PropertyMetadata(20d)); private static readonly DependencyPropertyKey ParentScaleWindowKey = DependencyProperty.RegisterAttachedReadOnly( "ParentScaleWindow", typeof(FontScaleWindow), typeof(FontScaleWindow), new FrameworkPropertyMetadata(default(FontScaleWindow), FrameworkPropertyMetadataOptions.None)); public static readonly DependencyProperty ParentScaleWindowProperty = ParentScaleWindowKey.DependencyProperty; private static FontScaleWindow? GetParentScaleWindow(DependencyObject element) => (FontScaleWindow)element.GetValue(ParentScaleWindowProperty); private static void SetParentScaleWindow(DependencyObject element, FontScaleWindow value) => element.SetValue(ParentScaleWindowKey, value); public static readonly DependencyProperty ResizeFontProperty = DependencyProperty.RegisterAttached( "ResizeFont", typeof(bool), typeof(FontScaleWindow), new FrameworkPropertyMetadata(false, ResizeCallBack)); public static void SetResizeFont(DependencyObject element, bool value) => element.SetValue(ResizeFontProperty, value); public static bool GetResizeFont(DependencyObject element) => (bool)element.GetValue(ResizeFontProperty); private static bool SetWindow(FrameworkElement el) { var w = GetWindow(el); if (w is not FontScaleWindow fsWindow) return false; SetParentScaleWindow(el, fsWindow); if (fsWindow._cachedObjects.Contains(el)) return true; fsWindow._cachedObjects.Add(el); el.Unloaded += ObjOnUnloaded; return true; } private static void ObjOnLoaded(object sender, RoutedEventArgs e) { var el = (FrameworkElement)sender; if (GetParentScaleWindow(el) == null) { if (!SetWindow(el)) { el.Loaded -= ObjOnLoaded; return; } } ResizeCallBack(sender, new DependencyPropertyChangedEventArgs()); } private static void ResizeCallBack(object? sender, DependencyPropertyChangedEventArgs eventArgs) { if (sender is not FrameworkElement obj) return; var window = GetParentScaleWindow(obj); if (window != null && !window._cachedObjects.Contains(obj)) { if (!SetWindow(obj)) return; } else if (window == null) { obj.Loaded += ObjOnLoaded; } if (window is not { _loaded: true }) return; CalculateFontSizeResized(obj); } private static void ObjOnUnloaded(object sender, RoutedEventArgs e) { var obj = (FrameworkElement)sender; var w = GetParentScaleWindow(obj)!; w._cachedObjects.Remove(obj); obj.Unloaded -= ObjOnUnloaded; obj.Loaded -= ObjOnLoaded; } private void OnSizeChanged(object sender, SizeChangedEventArgs e) { _currentDiagonal = _diagonal(e.NewSize.Width, e.NewSize.Height); foreach (var cached in _cachedObjects) { CalculateFontSizeResized(cached); } } //<-------------------Window--------------------> //<-------------------Scaling--------------------> public static readonly DependencyProperty ScaleProperty = DependencyProperty.RegisterAttached( "Scale", typeof(double), typeof(FontScaleWindow), new FrameworkPropertyMetadata(0.9999d ,FrameworkPropertyMetadataOptions.Inherits, ScalePropertyCallback)); public static void SetScale(DependencyObject element, double value) => element.SetValue(ScaleProperty, value); public static double GetScale(DependencyObject element) => (double)element.GetValue(ScaleProperty); private static void ScalePropertyCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) { var window = GetParentScaleWindow(d); if (window == null) { var w = GetWindow(d); if (w is FontScaleWindow fsWindow) { SetParentScaleWindow(d, fsWindow); } } ResizeElement(d); } public static void ResizeElement(DependencyObject d) { var set = d.ReadLocalValue(ScaleProperty) != DependencyProperty.UnsetValue; if (set) { if (GetResizeFont(d)) { CalculateFontSizeResized(d); } else { CalculateFontSize(d); } } } public static void CalculateFontSize(DependencyObject d) { var window = GetParentScaleWindow(d); if (window == null) return; var fontSize = GetScale(d) * window._defaultDiagonal; d.SetValue(FontSizeProperty, fontSize); } public static void CalculateFontSizeResized(DependencyObject d) { var window = GetParentScaleWindow(d); if (window == null) return; var windowsScale = Math.Pow(window._currentDiagonal / window._defaultDiagonal, window.ScaleCoefficient); var elScale = GetScale(d); var fontSize = window.DefaultFontSize * elScale * windowsScale; d.SetValue(FontSizeProperty, fontSize); } }