diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/BitmapIcon.cs b/Toolkit.UI.Controls.Avalonia/IconElement/BitmapIcon.cs new file mode 100644 index 0000000..d258a2e --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/BitmapIcon.cs @@ -0,0 +1,6 @@ +namespace Toolkit.UI.Controls.Avalonia; + +public class BitmapIcon : FluentAvalonia.UI.Controls.BitmapIcon +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/BitmapIconSource.cs b/Toolkit.UI.Controls.Avalonia/IconElement/BitmapIconSource.cs new file mode 100644 index 0000000..d9213f2 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/BitmapIconSource.cs @@ -0,0 +1,6 @@ +namespace Toolkit.UI.Controls.Avalonia; + +public class BitmapIconSource : FluentAvalonia.UI.Controls.BitmapIconSource +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/ContentIcon.cs b/Toolkit.UI.Controls.Avalonia/IconElement/ContentIcon.cs new file mode 100644 index 0000000..6873f11 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/ContentIcon.cs @@ -0,0 +1,71 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Templates; +using Avalonia.LogicalTree; +using Avalonia.Metadata; + +namespace Toolkit.UI.Controls.Avalonia; + +public class ContentIcon : FluentAvalonia.UI.Controls.FAIconElement +{ + public static readonly StyledProperty ContentProperty = + AvaloniaProperty.Register("Content"); + + public static readonly StyledProperty ContentTemplateProperty = + AvaloniaProperty.Register("ContentTemplate"); + + private ContentControl? content; + + [Content] + public object? Content + { + get => GetValue(ContentProperty); + set => SetValue(ContentProperty, value); + } + public IDataTemplate? IconTemplate + { + get => GetValue(ContentTemplateProperty); + set => SetValue(ContentTemplateProperty, value); + } + + protected override Size MeasureOverride(Size availableSize) + { + if (content == null) + { + CreateContent(); + } + + return base.MeasureOverride(availableSize); + + } + + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs args) + { + if (VisualChildren.Count > 0) + { + ((ILogical)VisualChildren[0]).NotifyAttachedToLogicalTree(args); + } + + base.OnAttachedToLogicalTree(args); + } + + protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs args) + { + if (VisualChildren.Count > 0) + { + ((ILogical)VisualChildren[0]).NotifyDetachedFromLogicalTree(args); + } + + base.OnDetachedFromLogicalTree(args); + } + private void CreateContent() + { + content = new ContentControl(); + + content.Bind(ContentControl.ContentProperty, this.GetBindingObservable(ContentProperty)); + content.Bind(ContentControl.ContentTemplateProperty, this.GetBindingObservable(ContentTemplateProperty)); + + LogicalChildren.Add(content); + VisualChildren.Add(content); + } +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/ContentIconSource.cs b/Toolkit.UI.Controls.Avalonia/IconElement/ContentIconSource.cs new file mode 100644 index 0000000..d92c967 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/ContentIconSource.cs @@ -0,0 +1,26 @@ +using Avalonia; +using Avalonia.Controls.Templates; +using Avalonia.Metadata; + +namespace Toolkit.UI.Controls.Avalonia; + +public class ContentIconSource : FluentAvalonia.UI.Controls.IconSource +{ + public static readonly StyledProperty ContentProperty = + AvaloniaProperty.Register("Content"); + + public static readonly StyledProperty ContentTemplateProperty = + AvaloniaProperty.Register("ContentTemplate"); + + [Content] + public object? Content + { + get => GetValue(ContentProperty); + set => SetValue(ContentProperty, value); + } + public IDataTemplate? IconTemplate + { + get => GetValue(ContentTemplateProperty); + set => SetValue(ContentTemplateProperty, value); + } +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/FAIconElement.cs b/Toolkit.UI.Controls.Avalonia/IconElement/FAIconElement.cs new file mode 100644 index 0000000..6964c15 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/FAIconElement.cs @@ -0,0 +1,6 @@ +namespace Toolkit.UI.Controls.Avalonia; + +public class FAIconElement : FluentAvalonia.UI.Controls.FAIconElement +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/FAPathIcon.cs b/Toolkit.UI.Controls.Avalonia/IconElement/FAPathIcon.cs new file mode 100644 index 0000000..c50e979 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/FAPathIcon.cs @@ -0,0 +1,6 @@ +namespace Toolkit.UI.Controls.Avalonia; + +public class FAPathIcon : FluentAvalonia.UI.Controls.FAPathIcon +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/FontIcon.cs b/Toolkit.UI.Controls.Avalonia/IconElement/FontIcon.cs new file mode 100644 index 0000000..de453cd --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/FontIcon.cs @@ -0,0 +1,6 @@ +namespace Toolkit.UI.Controls.Avalonia; + +public class FontIcon : FluentAvalonia.UI.Controls.FontIcon +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/FontIconSource.cs b/Toolkit.UI.Controls.Avalonia/IconElement/FontIconSource.cs new file mode 100644 index 0000000..6fb4e19 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/FontIconSource.cs @@ -0,0 +1,8 @@ +namespace Kromek.UI.Avalonia.Controls +{ + + public class FontIconSource : FluentAvalonia.UI.Controls.FontIconSource + { + + } +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/IconHelper.cs b/Toolkit.UI.Controls.Avalonia/IconElement/IconHelper.cs new file mode 100644 index 0000000..017a26d --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/IconHelper.cs @@ -0,0 +1,35 @@ +using System.Reflection; + +namespace Toolkit.UI.Controls.Avalonia; + +public class IconHelper +{ + private static MethodInfo? invoker; + + public static FluentAvalonia.UI.Controls.FAIconElement? CreateIconElement(FluentAvalonia.UI.Controls.IconSource source) + { + if (source is ContentIconSource contentIconSource) + { + ContentIcon contentIcon = new() + { + [!ContentIcon.ContentProperty] = contentIconSource[!ContentIconSource.ContentProperty], + [!ContentIcon.ContentTemplateProperty] = contentIconSource[!ContentIconSource.ContentTemplateProperty], + }; + + return contentIcon; + } + else + { + if (invoker == null) + { + Type? iconHelpersType = Type.GetType("FluentAvalonia.UI.Controls.IconHelpers,FluentAvalonia"); + if (iconHelpersType?.GetMethod("CreateFromUnknown", BindingFlags.Public | BindingFlags.Static) is MethodInfo createFromUnknown) + { + invoker = createFromUnknown; + } + } + + return (FluentAvalonia.UI.Controls.FAIconElement?)invoker?.Invoke(null, new object[] { source }); + } + } +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/ImageIcon.cs b/Toolkit.UI.Controls.Avalonia/IconElement/ImageIcon.cs new file mode 100644 index 0000000..5f89aff --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/ImageIcon.cs @@ -0,0 +1,6 @@ +namespace Toolkit.UI.Controls.Avalonia; + +public class ImageIcon : FluentAvalonia.UI.Controls.ImageIcon +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/ImageIconSource.cs b/Toolkit.UI.Controls.Avalonia/IconElement/ImageIconSource.cs new file mode 100644 index 0000000..4bb0ae4 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/ImageIconSource.cs @@ -0,0 +1,6 @@ +namespace Toolkit.UI.Controls.Avalonia; + +public class ImageIconSource : FluentAvalonia.UI.Controls.ImageIconSource +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/PathIconSource.cs b/Toolkit.UI.Controls.Avalonia/IconElement/PathIconSource.cs new file mode 100644 index 0000000..12a7c54 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/PathIconSource.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls.Templates; +using Avalonia.Controls; +using Avalonia.LogicalTree; +using Avalonia.Metadata; +using Avalonia; + +namespace Toolkit.UI.Controls.Avalonia; + +public class PathIconSource : FluentAvalonia.UI.Controls.PathIconSource +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/SymbolIcon.cs b/Toolkit.UI.Controls.Avalonia/IconElement/SymbolIcon.cs new file mode 100644 index 0000000..319badc --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/SymbolIcon.cs @@ -0,0 +1,6 @@ +namespace Toolkit.UI.Controls.Avalonia; + +public class SymbolIcon : FluentAvalonia.UI.Controls.SymbolIcon +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/SymbolIconSource.cs b/Toolkit.UI.Controls.Avalonia/IconElement/SymbolIconSource.cs new file mode 100644 index 0000000..7f426cf --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/SymbolIconSource.cs @@ -0,0 +1,6 @@ +namespace Toolkit.UI.Controls.Avalonia; + +public class SymbolIconSource : FluentAvalonia.UI.Controls.SymbolIconSource +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.axaml b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.axaml new file mode 100644 index 0000000..e40798d --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.axaml @@ -0,0 +1,75 @@ + + 1 + 1 + 1 + 2 + 0,-4,-4,0 + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.cs b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.cs new file mode 100644 index 0000000..69f8eb5 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.cs @@ -0,0 +1,379 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.Shapes; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using Kromek.UI.Avalonia.Controls; +using System; + +namespace Toolkit.UI.Controls.Avalonia; + +public class PersonPicture : TemplatedControl +{ + public static readonly StyledProperty BadgeGlyphProperty = + AvaloniaProperty.Register(nameof(BadgeGlyph)); + + public static readonly StyledProperty BadgeImageSourceProperty = + AvaloniaProperty.Register(nameof(BadgeImageSource)); + + public static readonly StyledProperty BadgeNumberProperty = + AvaloniaProperty.Register(nameof(BadgeNumber)); + + public static readonly StyledProperty BadgeTextProperty = + AvaloniaProperty.Register(nameof(BadgeText)); + + public static readonly StyledProperty DisplayNameProperty = + AvaloniaProperty.Register(nameof(DisplayName)); + + public static readonly StyledProperty InitialsProperty = + AvaloniaProperty.Register(nameof(Initials)); + + public static readonly StyledProperty IsGroupProperty = + AvaloniaProperty.Register(nameof(IsGroup)); + + public static readonly StyledProperty ProfilePictureProperty = + AvaloniaProperty.Register(nameof(ProfilePicture)); + + private static readonly StyledProperty TemplateSettingsProperty = + AvaloniaProperty.Register(nameof(TemplateSettings)); + + private FontIcon badgeGlyphIcon; + private ImageBrush badgeImageBrush; + private TextBlock badgeNumberTextBlock; + private Ellipse badgingBackgroundEllipse; + private Ellipse badgingEllipse; + private string displayNameInitials; + private TextBlock initialsTextBlock; + + public PersonPicture() + { + SetValue(TemplateSettingsProperty, new PersonPictureTemplateSettings()); + SizeChanged += OnSizeChanged; + } + + public string BadgeGlyph + { + get => GetValue(BadgeGlyphProperty); + set => SetValue(BadgeGlyphProperty, value); + } + + public IImage BadgeImageSource + { + get => GetValue(BadgeImageSourceProperty); + set => SetValue(BadgeImageSourceProperty, value); + } + + public int BadgeNumber + { + get => GetValue(BadgeNumberProperty); + set => SetValue(BadgeNumberProperty, value); + } + + public string BadgeText + { + get => GetValue(BadgeTextProperty); + set => SetValue(BadgeTextProperty, value); + } + + public string DisplayName + { + get => GetValue(DisplayNameProperty); + set => SetValue(DisplayNameProperty, value); + } + + public string Initials + { + get => GetValue(InitialsProperty); + set => SetValue(InitialsProperty, value); + } + + public bool IsGroup + { + get => GetValue(IsGroupProperty); + set => SetValue(IsGroupProperty, value); + } + + public IImage ProfilePicture + { + get => GetValue(ProfilePictureProperty); + set => SetValue(ProfilePictureProperty, value); + } + + public PersonPictureTemplateSettings TemplateSettings + { + get => GetValue(TemplateSettingsProperty); + set => SetValue(TemplateSettingsProperty, value); + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs args) + { + base.OnApplyTemplate(args); + + initialsTextBlock = args.NameScope.Get("InitialsTextBlock"); + + badgeNumberTextBlock = args.NameScope.Get("BadgeNumberTextBlock"); + badgeGlyphIcon = args.NameScope.Get("BadgeGlyphIcon"); + badgingEllipse = args.NameScope.Get("BadgingEllipse"); + badgingBackgroundEllipse = args.NameScope.Get("BadgingBackgroundEllipse"); + + UpdateBadge(); + UpdateIfReady(); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == BadgeGlyphProperty) + { + UpdateBadge(); + } + + if (change.Property == BadgeImageSourceProperty) + { + UpdateBadge(); + } + + if (change.Property == BadgeNumberProperty) + { + UpdateBadge(); + } + + if (change.Property == DisplayNameProperty) + { + UpdateDisplayName(); + } + + if (change.Property == InitialsProperty) + { + UpdateIfReady(); + } + + if (change.Property == IsGroupProperty) + { + UpdateIfReady(); + } + + if (change.Property == ProfilePictureProperty) + { + + } + } + + private IImage? GetImageSource() + { + if (ProfilePicture != null) + { + return ProfilePicture; + } + + return null; + } + + private string GetInitials() + { + if (!string.IsNullOrEmpty(Initials)) + { + return Initials; + } + else if (!string.IsNullOrEmpty(displayNameInitials)) + { + return displayNameInitials; + } + + return ""; + } + + private void OnSizeChanged(object? sender, SizeChangedEventArgs args) + { + { + bool widthChanged = args.NewSize.Width != args.PreviousSize.Width; + bool heightChanged = args.NewSize.Height != args.PreviousSize.Height; + double newSize; + + if (widthChanged && heightChanged) + { + newSize = args.NewSize.Width < args.NewSize.Height ? args.NewSize.Width : args.NewSize.Height; + } + else if (widthChanged) + { + newSize = args.NewSize.Width; + } + else if (heightChanged) + { + newSize = args.NewSize.Height; + } + else + { + return; + } + + Height = newSize; + Width = newSize; + } + + double fontSize = Math.Max(1.0, Width * .42); + + if (initialsTextBlock is not null) + { + initialsTextBlock.FontSize = fontSize; + } + + if (badgingEllipse is not null && badgingBackgroundEllipse is not null && badgeNumberTextBlock is not null && badgeGlyphIcon is not null) + { + double newSize = args.NewSize.Width < args.NewSize.Height ? args.NewSize.Width : args.NewSize.Height; + badgingEllipse.Height = newSize * 0.5; + badgingEllipse.Width = newSize * 0.5; + badgingBackgroundEllipse.Height = newSize * 0.5; + badgingBackgroundEllipse.Width = newSize * 0.5; + badgeNumberTextBlock.FontSize = Math.Max(1.0, badgingEllipse.Height * 0.6); + badgeGlyphIcon.FontSize = Math.Max(1.0, badgingEllipse.Height * 0.6); + } + } + + private void UpdateBadge() + { + if (BadgeImageSource != null) + { + UpdateBadgeImageSource(); + } + else if (BadgeNumber != 0) + { + UpdateBadgeNumber(); + } + else if (!string.IsNullOrEmpty(BadgeGlyph)) + { + UpdateBadgeGlyph(); + } + else + { + PseudoClasses.Set(":NoBadge", true); + if (badgeNumberTextBlock != null) + { + badgeNumberTextBlock.Text = ""; + } + + if (badgeGlyphIcon != null) + { + badgeGlyphIcon.Glyph = ""; + } + } + } + + private void UpdateBadgeGlyph() + { + if (badgingEllipse == null || badgeGlyphIcon == null) + { + return; + } + + if (string.IsNullOrEmpty(BadgeGlyph)) + { + PseudoClasses.Set(":NoBadge", true); + badgeGlyphIcon.Glyph = ""; + return; + } + + PseudoClasses.Set(":BadgeWithoutImageSource", true); + badgeGlyphIcon.Glyph = BadgeGlyph; + } + + private void UpdateBadgeImageSource() + { + if (badgingEllipse == null || badgeImageBrush == null) + { + return; + } + + badgeImageBrush.Source = (Bitmap?)BadgeImageSource; + + if (BadgeImageSource != null) + { + PseudoClasses.Set(":BadgeWithImageSource", true); + } + else + { + PseudoClasses.Set(":NoBadge", true); + } + } + private void UpdateBadgeNumber() + { + if (badgingEllipse == null || badgeNumberTextBlock == null) + { + return; + } + + if (BadgeNumber <= 0) + { + PseudoClasses.Set(":NoBadge", true); + badgeNumberTextBlock.Text = ""; + + return; + } + + PseudoClasses.Set(":BadgeWithoutImageSource", true); + if (BadgeNumber <= 99) + { + badgeNumberTextBlock.Text = BadgeNumber.ToString(); + } + else + { + badgeNumberTextBlock.Text = "99+"; + } + } + private void UpdateDisplayName() + { + displayNameInitials = PersonPictureInitialsGenerator.InitialsFromDisplayName(DisplayName); + UpdateIfReady(); + } + + private void UpdateIfReady() + { + string initials = GetInitials(); + IImage? imageSource = GetImageSource(); + + PersonPictureTemplateSettings templateSettings = TemplateSettings; + templateSettings.ActualInitials = initials; + + if (imageSource is not null) + { + ImageBrush? imageBrush = templateSettings.ActualImageBrush; + if (imageBrush == null) + { + imageBrush = new ImageBrush + { + Stretch = Stretch.UniformToFill + }; + + templateSettings.ActualImageBrush = imageBrush; + } + + imageBrush.Source = (Bitmap?)imageSource; + } + else + { + templateSettings.ActualImageBrush = null; + } + + if (IsGroup) + { + PseudoClasses.Set(":Group", true); + } + else + { + if (imageSource != null) + { + PseudoClasses.Set(":Photo", true); + } + else if (!string.IsNullOrEmpty(initials)) + { + PseudoClasses.Set(":Initials", true); + } + else + { + PseudoClasses.Set(":NoPhotoOrInitials", true); + } + } + } +} diff --git a/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureCharacterType.cs b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureCharacterType.cs new file mode 100644 index 0000000..9651afd --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureCharacterType.cs @@ -0,0 +1,9 @@ +namespace Toolkit.UI.Controls.Avalonia; + +internal enum PersonPictureCharacterType +{ + Other = 0, + Standard = 1, + Symbolic = 2, + Glyph = 3 +} diff --git a/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureInitialsGenerator.cs b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureInitialsGenerator.cs new file mode 100644 index 0000000..d0ba9ed --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureInitialsGenerator.cs @@ -0,0 +1,413 @@ +namespace Toolkit.UI.Controls.Avalonia; + +internal class PersonPictureInitialsGenerator +{ + public static PersonPictureCharacterType GetCharacterType(string str) + { + + PersonPictureCharacterType result = PersonPictureCharacterType.Other; + for (int i = 0; i < 3; i++) + { + if ((i >= str.Length) || (str[i] == '\0') || (str[i] == 0xFEFF)) + { + break; + } + + char character = str[i]; + PersonPictureCharacterType evaluationResult = GetCharacterType(character); + + switch (evaluationResult) + { + case PersonPictureCharacterType.Glyph: + result = PersonPictureCharacterType.Glyph; + break; + case PersonPictureCharacterType.Symbolic: + if (result != PersonPictureCharacterType.Glyph) + { + result = PersonPictureCharacterType.Symbolic; + } + break; + case PersonPictureCharacterType.Standard: + if ((result != PersonPictureCharacterType.Glyph) && (result != PersonPictureCharacterType.Symbolic)) + { + result = PersonPictureCharacterType.Standard; + } + break; + default: + break; + } + } + return result; + } + + public static PersonPictureCharacterType GetCharacterType(char character) + { + + // IPA Extensions + if ((character >= 0x0250) && (character <= 0x02AF)) + { + return PersonPictureCharacterType.Glyph; + } + // Arabic + if ((character >= 0x0600) && (character <= 0x06FF)) + { + return PersonPictureCharacterType.Glyph; + } + // Arabic Supplement + if ((character >= 0x0750) && (character <= 0x077F)) + { + return PersonPictureCharacterType.Glyph; + } + // Arabic Extended-A + if ((character >= 0x08A0) && (character <= 0x08FF)) + { + return PersonPictureCharacterType.Glyph; + } + // Arabic Presentation Forms-A + if ((character >= 0xFB50) && (character <= 0xFDFF)) + { + return PersonPictureCharacterType.Glyph; + } + // Arabic Presentation Forms-B + if ((character >= 0xFE70) && (character <= 0xFEFF)) + { + return PersonPictureCharacterType.Glyph; + } + // Devanagari + if ((character >= 0x0900) && (character <= 0x097F)) + { + return PersonPictureCharacterType.Glyph; + } + // Devanagari Extended + if ((character >= 0xA8E0) && (character <= 0xA8FF)) + { + return PersonPictureCharacterType.Glyph; + } + // Bengali + if ((character >= 0x0980) && (character <= 0x09FF)) + { + return PersonPictureCharacterType.Glyph; + } + // Gurmukhi + if ((character >= 0x0A00) && (character <= 0x0A7F)) + { + return PersonPictureCharacterType.Glyph; + } + // Gujarati + if ((character >= 0x0A80) && (character <= 0x0AFF)) + { + return PersonPictureCharacterType.Glyph; + } + // Oriya + if ((character >= 0x0B00) && (character <= 0x0B7F)) + { + return PersonPictureCharacterType.Glyph; + } + // Tamil + if ((character >= 0x0B80) && (character <= 0x0BFF)) + { + return PersonPictureCharacterType.Glyph; + } + // Telugu + if ((character >= 0x0C00) && (character <= 0x0C7F)) + { + return PersonPictureCharacterType.Glyph; + } + // Kannada + if ((character >= 0x0C80) && (character <= 0x0CFF)) + { + return PersonPictureCharacterType.Glyph; + } + // Malayalam + if ((character >= 0x0D00) && (character <= 0x0D7F)) + { + return PersonPictureCharacterType.Glyph; + } + // Sinhala + if ((character >= 0x0D80) && (character <= 0x0DFF)) + { + return PersonPictureCharacterType.Glyph; + } + // Thai + if ((character >= 0x0E00) && (character <= 0x0E7F)) + { + return PersonPictureCharacterType.Glyph; + } + // Lao + if ((character >= 0x0E80) && (character <= 0x0EFF)) + { + return PersonPictureCharacterType.Glyph; + } + // SYMBOLIC + // + // CJK Unified Ideographs + if ((character >= 0x4E00) && (character <= 0x9FFF)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Unified Ideographs Extension + if ((character >= 0x3400) && (character <= 0x4DBF)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Unified Ideographs Extension B + if ((character >= 0x20000) && (character <= 0x2A6DF)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Unified Ideographs Extension C + if ((character >= 0x2A700) && (character <= 0x2B73F)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Unified Ideographs Extension D + if ((character >= 0x2B740) && (character <= 0x2B81F)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Radicals Supplement + if ((character >= 0x2E80) && (character <= 0x2EFF)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Symbols and Punctuation + if ((character >= 0x3000) && (character <= 0x303F)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Strokes + if ((character >= 0x31C0) && (character <= 0x31EF)) + { + return PersonPictureCharacterType.Symbolic; + } + // Enclosed CJK Letters and Months + if ((character >= 0x3200) && (character <= 0x32FF)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Compatibility + if ((character >= 0x3300) && (character <= 0x33FF)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Compatibility Ideographs + if ((character >= 0xF900) && (character <= 0xFAFF)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Compatibility Forms + if ((character >= 0xFE30) && (character <= 0xFE4F)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Compatibility Ideographs Supplement + if ((character >= 0x2F800) && (character <= 0x2FA1F)) + { + return PersonPictureCharacterType.Symbolic; + } + // Greek and Coptic + if ((character >= 0x0370) && (character <= 0x03FF)) + { + return PersonPictureCharacterType.Symbolic; + } + // Hebrew + if ((character >= 0x0590) && (character <= 0x05FF)) + { + return PersonPictureCharacterType.Symbolic; + } + // Armenian + if ((character >= 0x0530) && (character <= 0x058F)) + { + return PersonPictureCharacterType.Symbolic; + } + // LATIN + // + // Basic Latin + if ((character > 0x0000) && (character <= 0x007F)) + { + return PersonPictureCharacterType.Standard; + } + // Latin-1 Supplement + if ((character >= 0x0080) && (character <= 0x00FF)) + { + return PersonPictureCharacterType.Standard; + } + // Latin Extended-A + if ((character >= 0x0100) && (character <= 0x017F)) + { + return PersonPictureCharacterType.Standard; + } + // Latin Extended-B + if ((character >= 0x0180) && (character <= 0x024F)) + { + return PersonPictureCharacterType.Standard; + } + // Latin Extended-C + if ((character >= 0x2C60) && (character <= 0x2C7F)) + { + return PersonPictureCharacterType.Standard; + } + // Latin Extended-D + if ((character >= 0xA720) && (character <= 0xA7FF)) + { + return PersonPictureCharacterType.Standard; + } + // Latin Extended-E + if ((character >= 0xAB30) && (character <= 0xAB6F)) + { + return PersonPictureCharacterType.Standard; + } + // Latin Extended Additional + if ((character >= 0x1E00) && (character <= 0x1EFF)) + { + return PersonPictureCharacterType.Standard; + } + // Cyrillic + if ((character >= 0x0400) && (character <= 0x04FF)) + { + return PersonPictureCharacterType.Standard; + } + // Cyrillic Supplement + if ((character >= 0x0500) && (character <= 0x052F)) + { + return PersonPictureCharacterType.Standard; + } + // Combining Diacritical Marks + if ((character >= 0x0300) && (character <= 0x036F)) + { + return PersonPictureCharacterType.Standard; + } + return PersonPictureCharacterType.Other; + } + + public static string InitialsFromDisplayName(string contactDisplayName) + { + PersonPictureCharacterType type = GetCharacterType(contactDisplayName); + if (type == PersonPictureCharacterType.Standard) + { + string displayName = contactDisplayName; + StripTrailingBrackets(ref displayName); + string[] words = Split(displayName, ' '); + + if (words.Length == 1) + { + string firstWord = words.First(); + string result = GetFirstFullCharacter(firstWord); + + return result.ToUpper(); + } + else if (words.Length > 1) + { + string firstWord = words.First(); + string lastWord = words.Last(); + string result = GetFirstFullCharacter(firstWord); + result += GetFirstFullCharacter(lastWord); + + return result.ToUpper(); + } + else + { + return string.Empty; + } + } + else + { + return string.Empty; + } + } + private static string GetFirstFullCharacter(string str) + { + int start = 0; + while (start < str.Length) + { + char character = str[start]; + // Omit ! " # $ % & ' ( ) * + , - . / + if ((character >= 0x0021) && (character <= 0x002F)) + { + start++; + continue; + } + // Omit : ; < = > ? @ + if ((character >= 0x003A) && (character <= 0x0040)) + { + start++; + continue; + } + // Omit { | } ~ + if ((character >= 0x007B) && (character <= 0x007E)) + { + start++; + continue; + } + break; + } + + if (start >= str.Length) + { + start = 0; + } + + int index = start + 1; + while (index < str.Length) + { + char character = str[index]; + + if ((character < 0x0300) || (character > 0x036F)) + { + break; + } + + index++; + } + + int strLength = index - start; + return SafeSubstring(str, start, strLength); + } + + private static string SafeSubstring(string value, int startIndex, int length) + { + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (startIndex > value.Length) + { + return string.Empty; + } + + if (length > value.Length - startIndex) + { + length = value.Length - startIndex; + } + + return value.Substring(startIndex, length); + } + private static string[] Split(string source, char delim, int maxIterations = 25) + { + return source.Split(new[] { delim }, maxIterations); + } + + private static void StripTrailingBrackets(ref string source) + { + string[] delimiters = { "{}", "()", "[]" }; + if (source.Length == 0) + { + return; + } + foreach (var delimiter in delimiters) + { + if (source[source.Length - 1] != delimiter[1]) + { + continue; + } + var start = source.LastIndexOf(delimiter[0]); + if (start == -1) + { + continue; + } + source = source.Remove(start); + return; + } + } +} diff --git a/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureTemplateSettings.cs b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureTemplateSettings.cs new file mode 100644 index 0000000..d7cf56f --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureTemplateSettings.cs @@ -0,0 +1,26 @@ +using Avalonia; +using Avalonia.Media; + +namespace Toolkit.UI.Controls.Avalonia; + +public class PersonPictureTemplateSettings : AvaloniaObject +{ + private static readonly StyledProperty ActualImageBrushProperty = + AvaloniaProperty.Register(nameof(ActualImageBrush)); + + + private static readonly StyledProperty ActualInitialsProperty = + AvaloniaProperty.Register(nameof(ActualInitials)); + + public ImageBrush? ActualImageBrush + { + get => GetValue(ActualImageBrushProperty); + set => SetValue(ActualImageBrushProperty, value); + } + + public string ActualInitials + { + get => GetValue(ActualInitialsProperty); + set => SetValue(ActualInitialsProperty, value); + } +}