1
1
using System ;
2
- using System . Collections . Generic ;
3
2
using System . Globalization ;
3
+ using System . Security . Cryptography ;
4
+ using System . Text ;
4
5
5
6
using Avalonia ;
6
7
using Avalonia . Controls ;
@@ -12,14 +13,6 @@ namespace SourceGit.Views
12
13
{
13
14
public class Avatar : Control , Models . IAvatarHost
14
15
{
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
-
23
16
public static readonly StyledProperty < Models . User > UserProperty =
24
17
AvaloniaProperty . Register < Avatar , Models . User > ( nameof ( User ) ) ;
25
18
@@ -29,11 +22,6 @@ public Models.User User
29
22
set => SetValue ( UserProperty , value ) ;
30
23
}
31
24
32
- static Avatar ( )
33
- {
34
- UserProperty . Changed . AddClassHandler < Avatar > ( OnUserPropertyChanged ) ;
35
- }
36
-
37
25
public Avatar ( )
38
26
{
39
27
var refetch = new MenuItem ( ) { Header = App . Text ( "RefetchAvatar" ) } ;
@@ -55,25 +43,72 @@ public override void Render(DrawingContext context)
55
43
return ;
56
44
57
45
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 )
60
50
{
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 ) ;
64
52
}
65
53
else
66
54
{
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
+ }
70
100
}
101
+
102
+ clip . Dispose ( ) ;
71
103
}
72
104
73
105
public void OnAvatarResourceChanged ( string email )
74
106
{
75
107
if ( User . Email . Equals ( email , StringComparison . Ordinal ) )
108
+ {
109
+ _img = Models . AvatarManager . Instance . Request ( User . Email , false ) ;
76
110
InvalidateVisual ( ) ;
111
+ }
77
112
}
78
113
79
114
protected override void OnLoaded ( RoutedEventArgs e )
@@ -88,53 +123,21 @@ protected override void OnUnloaded(RoutedEventArgs e)
88
123
Models . AvatarManager . Instance . Unsubscribe ( this ) ;
89
124
}
90
125
91
- private static void OnUserPropertyChanged ( Avatar avatar , AvaloniaPropertyChangedEventArgs e )
126
+ protected override void OnPropertyChanged ( AvaloniaPropertyChangedEventArgs change )
92
127
{
93
- if ( avatar . User == null )
94
- return ;
128
+ base . OnPropertyChanged ( change ) ;
95
129
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 )
103
131
{
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 ;
108
135
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
+ }
135
139
}
136
140
137
- private FormattedText _fallbackLabel = null ;
138
- private LinearGradientBrush _fallbackBrush = null ;
141
+ private Bitmap _img = null ;
139
142
}
140
143
}
0 commit comments