diff --git a/Avalonia.sln b/Avalonia.sln index 124f1e535d..7037a9721b 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -2003,6 +2003,30 @@ Global {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhone.Build.0 = Release|Any CPU {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.AppStore|iPhone.Build.0 = Debug|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Debug|iPhone.Build.0 = Debug|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|Any CPU.Build.0 = Release|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|iPhone.ActiveCfg = Release|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|iPhone.Build.0 = Release|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU diff --git a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs index e16e609a15..d6d0863854 100644 --- a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs +++ b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs @@ -1,7 +1,11 @@ using System; using System.Globalization; using System.Linq; +using Avalonia.Controls.Presenters; using Avalonia.Input; +using Avalonia.Input.GestureRecognizers; +using Avalonia.Interactivity; +using Avalonia.Media; using Avalonia.VisualTree; namespace Avalonia.Controls.Primitives @@ -58,18 +62,18 @@ namespace Avalonia.Controls.Primitives private Vector _offset; private bool _hasInit; private bool _suppressUpdateOffset; - private ListBoxItem _pressedItem; + private ScrollContentPresenter? _parentScroller; public DateTimePickerPanel() { FormatDate = DateTime.Now; - AddHandler(ListBoxItem.PointerPressedEvent, OnItemPointerDown, Avalonia.Interactivity.RoutingStrategies.Bubble); - AddHandler(ListBoxItem.PointerReleasedEvent, OnItemPointerUp, Avalonia.Interactivity.RoutingStrategies.Bubble); + AddHandler(TappedEvent, OnItemTapped, RoutingStrategies.Bubble); } static DateTimePickerPanel() { FocusableProperty.OverrideDefaultValue(true); + BackgroundProperty.OverrideDefaultValue(Brushes.Transparent); AffectsMeasure(ItemHeightProperty); } @@ -254,6 +258,8 @@ namespace Avalonia.Controls.Primitives _suppressUpdateOffset = true; SelectedValue = (int)newSel * Increment + MinimumValue; _suppressUpdateOffset = false; + + System.Diagnostics.Debug.WriteLine($"Offset: {_offset} ItemHeight: {ItemHeight}"); } } @@ -269,7 +275,7 @@ namespace Avalonia.Controls.Primitives public Size Extent => _extent; - public Size Viewport => new Size(0, ItemHeight); + public Size Viewport => Bounds.Size; public event EventHandler ScrollInvalidated; @@ -340,6 +346,20 @@ namespace Avalonia.Controls.Primitives return finalSize; } + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + _parentScroller = this.GetVisualParent() as ScrollContentPresenter; + _parentScroller?.AddHandler(Gestures.ScrollGestureEndedEvent, OnScrollGestureEnded); + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + _parentScroller?.RemoveHandler(Gestures.ScrollGestureEndedEvent, OnScrollGestureEnded); + _parentScroller = null; + } + protected override void OnKeyDown(KeyEventArgs e) { switch (e.Key) @@ -523,22 +543,13 @@ namespace Avalonia.Controls.Primitives return newValue; } - private void OnItemPointerDown(object sender, PointerPressedEventArgs e) + private void OnItemTapped(object sender, TappedEventArgs e) { - if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + if (e.Source is IVisual source && + GetItemFromSource(source) is ListBoxItem listBoxItem && + listBoxItem.Tag is int tag) { - _pressedItem = GetItemFromSource((IVisual)e.Source); - e.Handled = true; - } - } - - private void OnItemPointerUp(object sender, PointerReleasedEventArgs e) - { - if (e.GetCurrentPoint(this).Properties.PointerUpdateKind == PointerUpdateKind.LeftButtonReleased && - _pressedItem != null) - { - SelectedValue = (int)GetItemFromSource((IVisual)e.Source).Tag; - _pressedItem = null; + SelectedValue = tag; e.Handled = true; } } @@ -562,5 +573,15 @@ namespace Avalonia.Controls.Primitives { ScrollInvalidated?.Invoke(this, e); } + + private void OnScrollGestureEnded(object? sender, ScrollGestureEndedEventArgs e) + { + var snapY = Math.Round(Offset.Y / ItemHeight) * ItemHeight; + + if (snapY != Offset.Y) + { + Offset = Offset.WithY(snapY); + } + } } } diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index a62ba306ab..f2be15e686 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -371,7 +371,7 @@ namespace Avalonia.Controls.Presenters { var logicalUnits = delta.Y / logicalScrollItemSize.Y; delta = delta.WithY(delta.Y - logicalUnits * logicalScrollItemSize.Y); - dy = logicalUnits * scrollable!.ScrollSize.Height; + dy = logicalUnits; } else dy = delta.Y; @@ -389,7 +389,7 @@ namespace Avalonia.Controls.Presenters { var logicalUnits = delta.X / logicalScrollItemSize.X; delta = delta.WithX(delta.X - logicalUnits * logicalScrollItemSize.X); - dx = logicalUnits * scrollable!.ScrollSize.Width; + dx = logicalUnits; } else dx = delta.X; diff --git a/src/Skia/Avalonia.Skia/FontManagerImpl.cs b/src/Skia/Avalonia.Skia/FontManagerImpl.cs index 6b560ac739..572f7e7fab 100644 --- a/src/Skia/Avalonia.Skia/FontManagerImpl.cs +++ b/src/Skia/Avalonia.Skia/FontManagerImpl.cs @@ -29,8 +29,7 @@ namespace Avalonia.Skia [ThreadStatic] private static string[] t_languageTagBuffer; public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, - FontWeight fontWeight, - FontFamily fontFamily, CultureInfo culture, out Typeface fontKey) + FontWeight fontWeight, FontFamily fontFamily, CultureInfo culture, out Typeface fontKey) { SKFontStyle skFontStyle; @@ -109,15 +108,20 @@ namespace Avalonia.Skia if (typeface.FontFamily.Key == null) { var defaultName = SKTypeface.Default.FamilyName; - var fontStyle = new SKFontStyle((SKFontStyleWeight)typeface.Weight, SKFontStyleWidth.Normal, (SKFontStyleSlant)typeface.Style); + + var fontStyle = new SKFontStyle((SKFontStyleWeight)typeface.Weight, SKFontStyleWidth.Normal, + (SKFontStyleSlant)typeface.Style); foreach (var familyName in typeface.FontFamily.FamilyNames) { + if(familyName == FontFamily.DefaultFontFamilyName) + { + continue; + } + skTypeface = _skFontManager.MatchFamily(familyName, fontStyle); - if (skTypeface is null - || (!skTypeface.FamilyName.Equals(familyName, StringComparison.Ordinal) - && defaultName.Equals(skTypeface.FamilyName, StringComparison.Ordinal))) + if (skTypeface is null || defaultName.Equals(skTypeface.FamilyName, StringComparison.Ordinal)) { continue; } @@ -125,7 +129,10 @@ namespace Avalonia.Skia break; } - skTypeface ??= _skFontManager.MatchTypeface(SKTypeface.Default, fontStyle); + // MatchTypeface can return "null" if matched typeface wasn't found for the style + // Fallback to the default typeface and styles instead. + skTypeface ??= _skFontManager.MatchTypeface(SKTypeface.Default, fontStyle) + ?? SKTypeface.Default; } else { diff --git a/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs b/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs index 2b9d0b103e..ef048e4891 100644 --- a/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs +++ b/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs @@ -1,6 +1,4 @@ using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; using Avalonia.Media; using SkiaSharp; @@ -18,48 +16,131 @@ namespace Avalonia.Skia public SKTypeface Get(Typeface typeface) { - return GetNearestMatch(_typefaces, typeface); + return GetNearestMatch(typeface); } - private static SKTypeface GetNearestMatch(IDictionary typefaces, Typeface key) + private SKTypeface GetNearestMatch(Typeface key) { - if (typefaces.TryGetValue(key, out var typeface)) + if (_typefaces.Count == 0) { - return typeface; + return null; } - var initialWeight = (int)key.Weight; + if (_typefaces.TryGetValue(key, out var typeface)) + { + return typeface; + } + + if(key.Style != FontStyle.Normal) + { + key = new Typeface(key.FontFamily, FontStyle.Normal, key.Weight); + } + + if(TryFindWeightFallback(key, out typeface)) + { + return typeface; + } + + //Nothing was found so we try some regular typeface. + if (_typefaces.TryGetValue(new Typeface(key.FontFamily), out typeface)) + { + return typeface; + } + SKTypeface skTypeface = null; + + foreach(var pair in _typefaces) + { + skTypeface = pair.Value; + + if (skTypeface.FamilyName.Contains(key.FontFamily.Name)) + { + return skTypeface; + } + } + + return skTypeface; + } + + private bool TryFindWeightFallback(Typeface key, out SKTypeface typeface) + { + typeface = null; var weight = (int)key.Weight; - weight -= weight % 50; // make sure we start at a full weight + //If the target weight given is between 400 and 500 inclusive + if (weight >= 400 && weight <= 500) + { + //Look for available weights between the target and 500, in ascending order. + for (var i = 0; weight + i <= 500; i += 50) + { + if (_typefaces.TryGetValue(new Typeface(key.FontFamily, key.Style, (FontWeight)(weight + i)), out typeface)) + { + return true; + } + } - for (var i = 0; i < 2; i++) + //If no match is found, look for available weights less than the target, in descending order. + for (var i = 0; weight - i >= 100; i += 50) + { + if (_typefaces.TryGetValue(new Typeface(key.FontFamily, key.Style, (FontWeight)(weight - i)), out typeface)) + { + return true; + } + } + + //If no match is found, look for available weights greater than 500, in ascending order. + for (var i = 0; weight + i <= 900; i += 50) + { + if (_typefaces.TryGetValue(new Typeface(key.FontFamily, key.Style, (FontWeight)(weight + i)), out typeface)) + { + return true; + } + } + } + + //If a weight less than 400 is given, look for available weights less than the target, in descending order. + if (weight < 400) { - for (var j = 0; j < initialWeight; j += 50) + for (var i = 0; weight - i >= 100; i += 50) + { + if (_typefaces.TryGetValue(new Typeface(key.FontFamily, key.Style, (FontWeight)(weight - i)), out typeface)) + { + return true; + } + } + + //If no match is found, look for available weights less than the target, in descending order. + for (var i = 0; weight + i <= 900; i += 50) { - if (weight - j >= 100) + if (_typefaces.TryGetValue(new Typeface(key.FontFamily, key.Style, (FontWeight)(weight + i)), out typeface)) { - if (typefaces.TryGetValue(new Typeface(key.FontFamily, (FontStyle)i, (FontWeight)(weight - j)), out typeface)) - { - return typeface; - } + return true; } + } + } - if (weight + j > 900) + //If a weight greater than 500 is given, look for available weights greater than the target, in ascending order. + if (weight > 500) + { + for (var i = 0; weight + i <= 900; i += 50) + { + if (_typefaces.TryGetValue(new Typeface(key.FontFamily, key.Style, (FontWeight)(weight + i)), out typeface)) { - continue; + return true; } + } - if (typefaces.TryGetValue(new Typeface(key.FontFamily, (FontStyle)i, (FontWeight)(weight + j)), out typeface)) + //If no match is found, look for available weights less than the target, in descending order. + for (var i = 0; weight - i >= 100; i += 50) + { + if (_typefaces.TryGetValue(new Typeface(key.FontFamily, key.Style, (FontWeight)(weight - i)), out typeface)) { - return typeface; + return true; } } } - //Nothing was found so we try to get a regular typeface. - return typefaces.TryGetValue(new Typeface(key.FontFamily), out typeface) ? typeface : null; + return false; } } } diff --git a/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs index 2816146806..45fc7ba47e 100644 --- a/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs +++ b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs @@ -51,7 +51,7 @@ namespace Avalonia.Skia if (typeface == null) throw new InvalidOperationException("Typeface could not be loaded."); - if (typeface.FamilyName != fontFamily.Name) + if (!typeface.FamilyName.Contains(fontFamily.Name)) { continue; }