11using System ;
2- using System . Collections . Generic ;
32using System . Globalization ;
3+ using System . Security . Cryptography ;
4+ using System . Text ;
45
56using Avalonia ;
67using Avalonia . Controls ;
@@ -12,14 +13,6 @@ namespace SourceGit.Views
1213{
1314 public class Avatar : Control , Models . IAvatarHost
1415 {
15- private static readonly GradientStops [ ] FALLBACK_GRADIENTS = [
16- new GradientStops ( ) { new GradientStop ( Colors . Orange , 0 ) , new GradientStop ( Color . FromRgb ( 255 , 213 , 134 ) , 1 ) } ,
17- new GradientStops ( ) { new GradientStop ( Colors . DodgerBlue , 0 ) , new GradientStop ( Colors . LightSkyBlue , 1 ) } ,
18- new GradientStops ( ) { new GradientStop ( Colors . LimeGreen , 0 ) , new GradientStop ( Color . FromRgb ( 124 , 241 , 124 ) , 1 ) } ,
19- new GradientStops ( ) { new GradientStop ( Colors . Orchid , 0 ) , new GradientStop ( Color . FromRgb ( 248 , 161 , 245 ) , 1 ) } ,
20- new GradientStops ( ) { new GradientStop ( Colors . Tomato , 0 ) , new GradientStop ( Color . FromRgb ( 252 , 165 , 150 ) , 1 ) } ,
21- ] ;
22-
2316 public static readonly StyledProperty < Models . User > UserProperty =
2417 AvaloniaProperty . Register < Avatar , Models . User > ( nameof ( User ) ) ;
2518
@@ -29,11 +22,6 @@ public Models.User User
2922 set => SetValue ( UserProperty , value ) ;
3023 }
3124
32- static Avatar ( )
33- {
34- UserProperty . Changed . AddClassHandler < Avatar > ( OnUserPropertyChanged ) ;
35- }
36-
3725 public Avatar ( )
3826 {
3927 var refetch = new MenuItem ( ) { Header = App . Text ( "RefetchAvatar" ) } ;
@@ -55,25 +43,72 @@ public override void Render(DrawingContext context)
5543 return ;
5644
5745 var corner = ( float ) Math . Max ( 2 , Bounds . Width / 16 ) ;
58- var img = Models . AvatarManager . Instance . Request ( User . Email , false ) ;
59- if ( img != null )
46+ var rect = new Rect ( 0 , 0 , Bounds . Width , Bounds . Height ) ;
47+ var clip = context . PushClip ( new RoundedRect ( rect , corner ) ) ;
48+
49+ if ( _img != null )
6050 {
61- var rect = new Rect ( 0 , 0 , Bounds . Width , Bounds . Height ) ;
62- context . PushClip ( new RoundedRect ( rect , corner ) ) ;
63- context . DrawImage ( img , rect ) ;
51+ context . DrawImage ( _img , rect ) ;
6452 }
6553 else
6654 {
67- Point textOrigin = new Point ( ( Bounds . Width - _fallbackLabel . Width ) * 0.5 , ( Bounds . Height - _fallbackLabel . Height ) * 0.5 ) ;
68- context . DrawRectangle ( _fallbackBrush , null , new Rect ( 0 , 0 , Bounds . Width , Bounds . Height ) , corner , corner ) ;
69- context . DrawText ( _fallbackLabel , textOrigin ) ;
55+ context . DrawRectangle ( Brushes . White , new Pen ( new SolidColorBrush ( Colors . Black , 0.3f ) , 0.65f ) , rect , corner , corner ) ;
56+
57+ var offsetX = Bounds . Width / 8.0 ;
58+ var offsetY = Bounds . Height / 8.0 ;
59+
60+ var stepX = ( Bounds . Width - offsetX * 2 ) / 5.0 ;
61+ var stepY = ( Bounds . Height - offsetY * 2 ) / 5.0 ;
62+
63+ var user = User ;
64+ var lowered = user . Email . ToLower ( CultureInfo . CurrentCulture ) . Trim ( ) ;
65+ var hash = MD5 . HashData ( Encoding . Default . GetBytes ( lowered ) ) ;
66+
67+ var brush = new SolidColorBrush ( new Color ( 255 , hash [ 0 ] , hash [ 1 ] , hash [ 2 ] ) ) ;
68+ var switches = new bool [ 15 ] ;
69+ for ( int i = 0 ; i < switches . Length ; i ++ )
70+ switches [ i ] = hash [ i + 1 ] % 2 == 1 ;
71+
72+ for ( int row = 0 ; row < 5 ; row ++ )
73+ {
74+ var x = offsetX + stepX * 2 ;
75+ var y = offsetY + stepY * row ;
76+ var idx = row * 3 ;
77+
78+ if ( switches [ idx ] )
79+ context . FillRectangle ( brush , new Rect ( x , y , stepX , stepY ) ) ;
80+
81+ if ( switches [ idx + 1 ] )
82+ context . FillRectangle ( brush , new Rect ( x + stepX , y , stepX , stepY ) ) ;
83+
84+ if ( switches [ idx + 2 ] )
85+ context . FillRectangle ( brush , new Rect ( x + stepX * 2 , y , stepX , stepY ) ) ;
86+ }
87+
88+ for ( int row = 0 ; row < 5 ; row ++ )
89+ {
90+ var x = offsetX ;
91+ var y = offsetY + stepY * row ;
92+ var idx = row * 3 + 2 ;
93+
94+ if ( switches [ idx ] )
95+ context . FillRectangle ( brush , new Rect ( x , y , stepX , stepY ) ) ;
96+
97+ if ( switches [ idx - 1 ] )
98+ context . FillRectangle ( brush , new Rect ( x + stepX , y , stepX , stepY ) ) ;
99+ }
70100 }
101+
102+ clip . Dispose ( ) ;
71103 }
72104
73105 public void OnAvatarResourceChanged ( string email )
74106 {
75107 if ( User . Email . Equals ( email , StringComparison . Ordinal ) )
108+ {
109+ _img = Models . AvatarManager . Instance . Request ( User . Email , false ) ;
76110 InvalidateVisual ( ) ;
111+ }
77112 }
78113
79114 protected override void OnLoaded ( RoutedEventArgs e )
@@ -88,53 +123,21 @@ protected override void OnUnloaded(RoutedEventArgs e)
88123 Models . AvatarManager . Instance . Unsubscribe ( this ) ;
89124 }
90125
91- private static void OnUserPropertyChanged ( Avatar avatar , AvaloniaPropertyChangedEventArgs e )
126+ protected override void OnPropertyChanged ( AvaloniaPropertyChangedEventArgs change )
92127 {
93- if ( avatar . User == null )
94- return ;
128+ base . OnPropertyChanged ( change ) ;
95129
96- var fallback = GetFallbackString ( avatar . User . Name ) ;
97- var chars = fallback . ToCharArray ( ) ;
98- var sum = 0 ;
99- foreach ( var c in chars )
100- sum += Math . Abs ( c ) ;
101-
102- avatar . _fallbackBrush = new LinearGradientBrush
130+ if ( change . Property == UserProperty )
103131 {
104- GradientStops = FALLBACK_GRADIENTS [ sum % FALLBACK_GRADIENTS . Length ] ,
105- StartPoint = new RelativePoint ( 0 , 0 , RelativeUnit . Relative ) ,
106- EndPoint = new RelativePoint ( 0 , 1 , RelativeUnit . Relative ) ,
107- } ;
132+ var user = User ;
133+ if ( user == null )
134+ return ;
108135
109- var typeface = new Typeface ( "fonts:SourceGit#JetBrains Mono" ) ;
110- avatar . _fallbackLabel = new FormattedText (
111- fallback ,
112- CultureInfo . CurrentCulture ,
113- FlowDirection . LeftToRight ,
114- typeface ,
115- avatar . Width * 0.65 ,
116- Brushes . White ) ;
117-
118- avatar . InvalidateVisual ( ) ;
119- }
120-
121- private static string GetFallbackString ( string name )
122- {
123- if ( string . IsNullOrWhiteSpace ( name ) )
124- return "?" ;
125-
126- var parts = name . Split ( ' ' , StringSplitOptions . RemoveEmptyEntries ) ;
127- var chars = new List < char > ( ) ;
128- foreach ( var part in parts )
129- chars . Add ( part [ 0 ] ) ;
130-
131- if ( chars . Count >= 2 && char . IsAsciiLetterOrDigit ( chars [ 0 ] ) && char . IsAsciiLetterOrDigit ( chars [ ^ 1 ] ) )
132- return string . Format ( "{0}{1}" , chars [ 0 ] , chars [ ^ 1 ] ) ;
133-
134- return name . Substring ( 0 , 1 ) ;
136+ _img = Models . AvatarManager . Instance . Request ( User . Email , false ) ;
137+ InvalidateVisual ( ) ;
138+ }
135139 }
136140
137- private FormattedText _fallbackLabel = null ;
138- private LinearGradientBrush _fallbackBrush = null ;
141+ private Bitmap _img = null ;
139142 }
140143}
0 commit comments