@@ -18,6 +18,7 @@ public class RenderItem
1818 public bool IsHead { get ; set ; } = false ;
1919 public double Width { get ; set ; } = 0.0 ;
2020 public Models . Decorator Decorator { get ; set ; } = null ;
21+ public List < FormattedText > Remotes { get ; set ; } = [ ] ;
2122 }
2223
2324 public static readonly StyledProperty < FontFamily > FontFamilyProperty =
@@ -130,7 +131,6 @@ public override void Render(DrawingContext context)
130131 }
131132
132133 var entireRect = new RoundedRect ( new Rect ( x , y , item . Width , 16 ) , new CornerRadius ( 4 ) ) ;
133-
134134 if ( item . IsHead )
135135 {
136136 if ( useGraphColor )
@@ -141,24 +141,32 @@ public override void Render(DrawingContext context)
141141 using ( context . PushOpacity ( .6 ) )
142142 context . DrawRectangle ( item . Brush , null , entireRect ) ;
143143 }
144-
145- context . DrawText ( item . Label , new Point ( x + 16 , y + 8.0 - item . Label . Height * 0.5 ) ) ;
146144 }
147145 else
148146 {
149147 if ( bg != null )
150148 context . DrawRectangle ( bg , null , entireRect ) ;
151149
152- var labelRect = new RoundedRect ( new Rect ( x + 16 , y , item . Label . Width + 8 , 16 ) , new CornerRadius ( 0 , 4 , 4 , 0 ) ) ;
150+ var labelRect = new RoundedRect ( new Rect ( x + 16 , y , item . Width - 16 , 16 ) , new CornerRadius ( 4 , 0 , 0 , 4 ) ) ;
153151 using ( context . PushOpacity ( .2 ) )
154152 context . DrawRectangle ( item . Brush , null , labelRect ) ;
153+ }
154+
155+ context . DrawLine ( new Pen ( item . Brush ) , new Point ( x + 16 , y ) , new Point ( x + 16 , y + 16 ) ) ;
156+ context . DrawText ( item . Label , new Point ( x + 20 , y + 8.0 - item . Label . Height * 0.5 ) ) ;
155157
156- context . DrawLine ( new Pen ( item . Brush ) , new Point ( x + 16 , y ) , new Point ( x + 16 , y + 16 ) ) ;
157- context . DrawText ( item . Label , new Point ( x + 20 , y + 8.0 - item . Label . Height * 0.5 ) ) ;
158+ if ( item . Remotes . Count > 0 )
159+ {
160+ var rx = x + 20 + item . Label . WidthIncludingTrailingWhitespace + 4 ;
161+ foreach ( var remote in item . Remotes )
162+ {
163+ context . DrawLine ( new Pen ( item . Brush ) , new Point ( rx , y ) , new Point ( rx , y + 16 ) ) ;
164+ context . DrawText ( remote , new Point ( rx + 4 , y + 8.0 - remote . Height * 0.5 ) ) ;
165+ rx += remote . WidthIncludingTrailingWhitespace + 9 ;
166+ }
158167 }
159168
160169 context . DrawRectangle ( null , new Pen ( item . Brush ) , entireRect ) ;
161-
162170 using ( context . PushTransform ( Matrix . CreateTranslation ( x + 3 , y + 3 ) ) )
163171 context . DrawGeometry ( fg , null , item . Icon ) ;
164172
@@ -180,94 +188,150 @@ protected override Size MeasureOverride(Size availableSize)
180188 return new Size ( 0 , 0 ) ;
181189
182190 var refs = commit . Decorators ;
183- if ( refs is { Count : > 0 } )
191+ var count = refs . Count ;
192+ if ( count == 0 )
184193 {
185- var typeface = new Typeface ( FontFamily ) ;
186- var typefaceBold = new Typeface ( FontFamily , FontStyle . Normal , FontWeight . Bold ) ;
187- var fg = Foreground ;
188- var normalBG = UseGraphColor ? Models . CommitGraph . Pens [ commit . Color ] . Brush : Brushes . Gray ;
189- var labelSize = FontSize ;
190- var requiredHeight = 16.0 ;
191- var x = 0.0 ;
192- var allowWrap = AllowWrap ;
193- var showTags = ShowTags ;
194-
195- foreach ( var decorator in refs )
196- {
197- if ( ! showTags && decorator . Type == Models . DecoratorType . Tag )
198- continue ;
194+ InvalidateVisual ( ) ;
195+ return new Size ( 0 , 0 ) ;
196+ }
197+
198+ var typeface = new Typeface ( FontFamily ) ;
199+ var typefaceHead = new Typeface ( FontFamily , FontStyle . Normal , FontWeight . Bold ) ;
200+ var typefaceRemote = new Typeface ( FontFamily , FontStyle . Italic , FontWeight . Bold ) ;
201+ var fg = Foreground ;
202+ var normalBG = UseGraphColor ? Models . CommitGraph . Pens [ commit . Color ] . Brush : Brushes . Gray ;
203+ var labelSize = FontSize ;
204+ var requiredHeight = 16.0 ;
205+ var x = 0.0 ;
206+ var allowWrap = AllowWrap ;
207+ var showTags = ShowTags ;
208+ var skippedIdx = new HashSet < int > ( ) ;
199209
200- var isHead = decorator . Type is Models . DecoratorType . CurrentBranchHead or Models . DecoratorType . CurrentCommitHead ;
210+ for ( var i = 0 ; i < count ; i ++ )
211+ {
212+ if ( skippedIdx . Contains ( i ) )
213+ continue ;
201214
202- var label = new FormattedText (
215+ var decorator = refs [ i ] ;
216+ if ( ! showTags && decorator . Type == Models . DecoratorType . Tag )
217+ continue ;
218+
219+ var item = new RenderItem ( ) { Brush = normalBG , Decorator = decorator } ;
220+ var findRemotes = true ;
221+ _items . Add ( item ) ;
222+
223+ switch ( decorator . Type )
224+ {
225+ case Models . DecoratorType . CurrentBranchHead :
226+ case Models . DecoratorType . CurrentCommitHead :
227+ item . Icon = LoadIcon ( "Icons.Head" ) ;
228+ item . IsHead = true ;
229+ break ;
230+ case Models . DecoratorType . RemoteBranchHead :
231+ findRemotes = false ;
232+ item . Icon = LoadIcon ( "Icons.Remote" ) ;
233+ break ;
234+ case Models . DecoratorType . Tag :
235+ item . Brush = Brushes . Gray ;
236+ findRemotes = false ;
237+ item . Icon = LoadIcon ( "Icons.Tag" ) ;
238+ break ;
239+ default :
240+ item . Icon = LoadIcon ( "Icons.Branch" ) ;
241+ break ;
242+ }
243+
244+ if ( item . IsHead )
245+ {
246+ item . Label = new FormattedText (
203247 decorator . Name ,
204248 CultureInfo . CurrentCulture ,
205249 FlowDirection . LeftToRight ,
206- isHead ? typefaceBold : typeface ,
207- isHead ? labelSize + 1 : labelSize ,
250+ typefaceHead ,
251+ labelSize + 1 ,
208252 fg ) ;
253+ }
254+ else
255+ {
256+ item . Label = new FormattedText (
257+ decorator . Name ,
258+ CultureInfo . CurrentCulture ,
259+ FlowDirection . LeftToRight ,
260+ typeface ,
261+ labelSize ,
262+ fg ) ;
263+ }
209264
210- var item = new RenderItem ( )
211- {
212- Label = label ,
213- Brush = normalBG ,
214- IsHead = isHead ,
215- Decorator = decorator ,
216- } ;
217-
218- StreamGeometry geo ;
219- switch ( decorator . Type )
220- {
221- case Models . DecoratorType . CurrentBranchHead :
222- case Models . DecoratorType . CurrentCommitHead :
223- geo = this . FindResource ( "Icons.Head" ) as StreamGeometry ;
224- break ;
225- case Models . DecoratorType . RemoteBranchHead :
226- geo = this . FindResource ( "Icons.Remote" ) as StreamGeometry ;
227- break ;
228- case Models . DecoratorType . Tag :
229- item . Brush = Brushes . Gray ;
230- geo = this . FindResource ( "Icons.Tag" ) as StreamGeometry ;
231- break ;
232- default :
233- geo = this . FindResource ( "Icons.Branch" ) as StreamGeometry ;
234- break ;
235- }
265+ item . Width = item . Label . Width + 24 ;
236266
237- var drawGeo = geo ! . Clone ( ) ;
238- var iconBounds = drawGeo . Bounds ;
239- var translation = Matrix . CreateTranslation ( - ( Vector ) iconBounds . Position ) ;
240- var scale = Math . Min ( 10.0 / iconBounds . Width , 10.0 / iconBounds . Height ) ;
241- var transform = translation * Matrix . CreateScale ( scale , scale ) ;
242- if ( drawGeo . Transform == null || drawGeo . Transform . Value == Matrix . Identity )
243- drawGeo . Transform = new MatrixTransform ( transform ) ;
244- else
245- drawGeo . Transform = new MatrixTransform ( drawGeo . Transform . Value * transform ) ;
246-
247- item . Icon = drawGeo ;
248- item . Width = 16 + ( isHead ? 0 : 4 ) + label . Width + 4 ;
249- _items . Add ( item ) ;
250-
251- x += item . Width + 4 ;
252- if ( allowWrap )
267+ if ( findRemotes )
268+ {
269+ for ( var j = i + 1 ; j < count ; j ++ )
253270 {
254- if ( x > availableSize . Width )
271+ var test = refs [ j ] ;
272+ if ( test . Type != Models . DecoratorType . RemoteBranchHead )
273+ continue ;
274+
275+ var idxOfSlash = test . Name . IndexOf ( '/' ) ;
276+ if ( idxOfSlash < 1 || idxOfSlash == test . Name . Length - 1 )
277+ continue ;
278+
279+ var name = test . Name . Substring ( idxOfSlash + 1 ) ;
280+ if ( decorator . Name . Equals ( name , StringComparison . Ordinal ) )
255281 {
256- requiredHeight += 20.0 ;
257- x = item . Width ;
282+ var remote = new FormattedText (
283+ test . Name . Substring ( 0 , idxOfSlash ) ,
284+ CultureInfo . CurrentCulture ,
285+ FlowDirection . LeftToRight ,
286+ typefaceRemote ,
287+ labelSize ,
288+ fg ) ;
289+
290+ item . Remotes . Add ( remote ) ;
291+ item . Width += remote . Width + 9 ;
292+ skippedIdx . Add ( j ) ;
258293 }
259294 }
260295 }
261296
262- var requiredWidth = allowWrap && requiredHeight > 16.0
263- ? ( double . IsInfinity ( availableSize . Width ) ? x + 2 : availableSize . Width )
264- : x + 2 ;
265- InvalidateVisual ( ) ;
266- return new Size ( requiredWidth , requiredHeight ) ;
297+ x += item . Width + 4 ;
298+ if ( allowWrap )
299+ {
300+ if ( x > availableSize . Width )
301+ {
302+ requiredHeight += 20.0 ;
303+ x = item . Width ;
304+ }
305+ }
306+ }
307+
308+ double requiredWidth = 0 ;
309+ if ( _items . Count > 0 )
310+ {
311+ if ( allowWrap && requiredHeight > 16.0 )
312+ requiredWidth = double . IsInfinity ( availableSize . Width ) ? x + 2 : availableSize . Width ;
313+ else
314+ requiredWidth = x + 2 ;
267315 }
268316
269317 InvalidateVisual ( ) ;
270- return new Size ( 0 , 0 ) ;
318+ return new Size ( requiredWidth , requiredHeight ) ;
319+ }
320+
321+ private Geometry LoadIcon ( string resourceKey )
322+ {
323+ var geo = this . FindResource ( resourceKey ) as StreamGeometry ;
324+ var drawGeo = geo ! . Clone ( ) ;
325+ var iconBounds = drawGeo . Bounds ;
326+ var translation = Matrix . CreateTranslation ( - ( Vector ) iconBounds . Position ) ;
327+ var scale = Math . Min ( 10.0 / iconBounds . Width , 10.0 / iconBounds . Height ) ;
328+ var transform = translation * Matrix . CreateScale ( scale , scale ) ;
329+ if ( drawGeo . Transform == null || drawGeo . Transform . Value == Matrix . Identity )
330+ drawGeo . Transform = new MatrixTransform ( transform ) ;
331+ else
332+ drawGeo . Transform = new MatrixTransform ( drawGeo . Transform . Value * transform ) ;
333+
334+ return drawGeo ;
271335 }
272336
273337 private List < RenderItem > _items = new List < RenderItem > ( ) ;
0 commit comments