Skip to content

Commit 0ec2203

Browse files
committed
#31 Implemented MacOs badge renderer.
1 parent c3fdb7c commit 0ec2203

File tree

3 files changed

+187
-38
lines changed

3 files changed

+187
-38
lines changed

Source/Plugin.Badge.Mac/BadgeView.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using System.Linq;
2+
using AppKit;
3+
using Xamarin.Forms;
4+
using Xamarin.Forms.Platform.MacOS;
5+
6+
namespace Plugin.Badge.Mac
7+
{
8+
/// <summary>
9+
/// A badge view with ediatble properties that can be added to any view.
10+
/// </summary>
11+
public class BadgeView : NSTextField
12+
{
13+
public string Text
14+
{
15+
get => StringValue;
16+
set
17+
{
18+
StringValue = value;
19+
Hidden = string.IsNullOrEmpty(value);
20+
SizeToFit();
21+
}
22+
}
23+
24+
public Color Color
25+
{
26+
set => BackgroundColor = value == Color.Default ? Color.Red.ToNSColor() : value.ToNSColor();
27+
}
28+
29+
public new Color TextColor
30+
{
31+
set => base.TextColor = value == Color.Default ? NSColor.White : value.ToNSColor();
32+
}
33+
34+
public new Font Font
35+
{
36+
set => base.Font = value == Font.Default ? NSFont.SystemFontOfSize(NSFont.SmallSystemFontSize) : value.ToNSFont();
37+
}
38+
39+
/// <summary>
40+
/// Creates and adds a badge to the target view.
41+
/// </summary>
42+
/// <param name="target">Target view to add badge to.</param>
43+
/// <param name="alignRelativeToContent">Tries to align the badge relative to the first contecnt of the target</param>
44+
public BadgeView(NSView target, bool alignRelativeToContent = false)
45+
{
46+
Alignment = NSTextAlignment.Center;
47+
Editable = false;
48+
Text = string.Empty;
49+
TranslatesAutoresizingMaskIntoConstraints = false;
50+
Font = Font.Default;
51+
Bordered = false;
52+
LineBreakMode = NSLineBreakMode.TruncatingTail;
53+
UsesSingleLineMode = true;
54+
WantsLayer = true;
55+
Layer.CornerRadius = 7.5f;
56+
target.AddSubview(this);
57+
58+
WidthAnchor.ConstraintLessThanOrEqualToConstant(35).Active = true;
59+
60+
61+
if (alignRelativeToContent && target.Subviews.Any())
62+
{
63+
LeftAnchor.ConstraintEqualToAnchor(target.Subviews.First().RightAnchor, 0).Active = true;
64+
CenterYAnchor.ConstraintEqualToAnchor(target.Subviews.First().CenterYAnchor).Active = true;
65+
}
66+
else
67+
{
68+
TopAnchor.ConstraintEqualToAnchor(target.TopAnchor, 0).Active = true;
69+
RightAnchor.ConstraintEqualToAnchor(target.RightAnchor, 0).Active = true;
70+
}
71+
}
72+
}
73+
}
Lines changed: 113 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,147 @@
11
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
25
using AppKit;
6+
using Plugin.Badge.Abstractions;
7+
using Xamarin.Forms;
38
using Xamarin.Forms.Platform.MacOS;
49

510
namespace Plugin.Badge.Mac
611
{
712
public class BadgedTabbedPageRenderer : TabbedPageRenderer
813
{
9-
public BadgedTabbedPageRenderer()
10-
{
11-
}
14+
protected readonly Dictionary<Element, BadgeView> BadgeViews = new Dictionary<Element, BadgeView>();
15+
private NSSegmentedControl _segmentedControl;
1216

1317
protected override void OnElementChanged(VisualElementChangedEventArgs e)
1418
{
15-
1619
base.OnElementChanged(e);
20+
}
21+
22+
public override void ViewWillAppear()
23+
{
24+
base.ViewWillAppear();
25+
26+
27+
Cleanup(Tabbed);
28+
1729

18-
foreach(var item in this.TabViewItems){
19-
item.ToolTip = "bla";
30+
Tabbed.ChildAdded += OnTabAdded;
31+
Tabbed.ChildRemoved += OnTabRemoved;
32+
33+
_segmentedControl = this.View.Subviews.FirstOrDefault(s => s is NSSegmentedControl) as NSSegmentedControl;
34+
if (_segmentedControl == null)
35+
{
36+
Console.WriteLine("[TabBadge] No SegmentedControl found. Not adding tabs.");
2037
}
2138

2239

40+
_segmentedControl.SegmentStyle = NSSegmentStyle.TexturedSquare;
2341

42+
var tabWidth = this.View.Frame.Width / _segmentedControl.SegmentCount;
2443

44+
for (var i = 0; i < Tabbed.Children.Count; i++)
45+
{
46+
AddTabBadge(i);
47+
}
2548
}
2649

27-
public override void ViewWillAppear()
50+
protected virtual void AddTabBadge(int tabIndex)
2851
{
29-
base.ViewWillAppear();
52+
var segment = _segmentedControl.Subviews[tabIndex];
53+
54+
var element = Tabbed.Children[tabIndex];
55+
element.PropertyChanged += OnTabbedPagePropertyChanged;
56+
57+
var badge = new BadgeView(segment, false)
58+
{
59+
Color = TabBadge.GetBadgeColor(element),
60+
TextColor = TabBadge.GetBadgeTextColor(element),
61+
Text = TabBadge.GetBadgeText(element)
62+
};
63+
64+
BadgeViews.Add(element, badge);
65+
}
3066

67+
protected virtual void OnTabbedPagePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
68+
{
69+
var element = sender as Element;
70+
if (element == null)
71+
return;
3172

32-
//foreach (var item in this.TabViewItems)
33-
//{
34-
// item.ToolTip = "blabla";
35-
// item.View.Layer.BackgroundColor = new CoreGraphics.CGColor(0.1f, 0.5f, 0.7f);
36-
37-
// Console.WriteLine(item.View.GetType().Name);
38-
// foreach(var view in item.View.Subviews){
39-
// Console.WriteLine(view.GetType().Name);
40-
// }
41-
//}
73+
if (!BadgeViews.TryGetValue(element, out BadgeView badgeView))
74+
{
75+
return;
76+
}
4277

43-
foreach (var view in this.View.Subviews)
44-
{
45-
Console.WriteLine(view.GetType().Name);
46-
if(view is NSSegmentedControl){
47-
var segmentedControl = view as NSSegmentedControl;
48-
segmentedControl.SegmentStyle = NSSegmentStyle.TexturedSquare;
78+
if (e.PropertyName == TabBadge.BadgeTextProperty.PropertyName)
79+
{
80+
badgeView.Text = TabBadge.GetBadgeText(element);
81+
return;
82+
}
4983

50-
foreach (var segment in segmentedControl.Subviews)
51-
{
52-
53-
var badge = new NSView(new CoreGraphics.CGRect(0, 0, 20, 20));
54-
badge.WantsLayer = true;
55-
badge.Layer.BackgroundColor = new CoreGraphics.CGColor(1f, 0f, 0f);
56-
//badge.AddConstraints(new []{});
84+
if (e.PropertyName == TabBadge.BadgeColorProperty.PropertyName)
85+
{
86+
badgeView.Color = TabBadge.GetBadgeColor(element);
87+
return;
88+
}
5789

58-
segment.AddSubview(badge);
59-
var constraint = NSLayoutConstraint.Create(badge, NSLayoutAttribute.Right, NSLayoutRelation.Equal);
60-
badge.AddConstraint(constraint);
90+
if (e.PropertyName == TabBadge.BadgeTextColorProperty.PropertyName)
91+
{
92+
badgeView.TextColor = TabBadge.GetBadgeTextColor(element);
93+
return;
94+
}
95+
96+
if (e.PropertyName == TabBadge.BadgeFontProperty.PropertyName)
97+
{
98+
badgeView.Font = TabBadge.GetBadgeFont(element);
99+
return;
100+
}
101+
}
102+
103+
private void OnTabRemoved(object sender, ElementEventArgs e)
104+
{
105+
e.Element.PropertyChanged -= OnTabbedPagePropertyChanged;
106+
BadgeViews.Remove(e.Element);
107+
}
108+
109+
private async void OnTabAdded(object sender, ElementEventArgs e)
110+
{
111+
//workaround for XF, tabbar is not updated at this point and we have no way of knowing for sure when it will be updated. so we have to wait ...
112+
await Task.Delay(10);
61113

114+
var page = e.Element as Page;
115+
if (page == null)
116+
return;
62117

63-
Console.WriteLine(segment.GetType().Name);
64-
}
65-
}
66-
}
118+
var tabIndex = Tabbed.Children.IndexOf(page);
119+
AddTabBadge(tabIndex);
120+
}
121+
122+
protected override void Dispose(bool disposing)
123+
{
124+
Cleanup(Tabbed);
125+
126+
base.Dispose(disposing);
127+
}
67128

129+
private void Cleanup(TabbedPage page)
130+
{
131+
if (page == null)
132+
{
133+
return;
134+
}
135+
136+
foreach (var tab in page.Children)
137+
{
138+
tab.PropertyChanged -= OnTabbedPagePropertyChanged;
139+
}
68140

141+
page.ChildRemoved -= OnTabRemoved;
142+
page.ChildAdded -= OnTabAdded;
69143

144+
BadgeViews.Clear();
70145
}
71146
}
72147
}

Source/Plugin.Badge.Mac/Plugin.Badge.Mac.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
<ItemGroup>
6565
<Compile Include="BadgedTabbedPageRenderer.cs" />
6666
<Compile Include="Properties\AssemblyInfo.cs" />
67+
<Compile Include="BadgeView.cs" />
6768
</ItemGroup>
6869
<ItemGroup>
6970
<ProjectReference Include="..\Plugin.Badge.Abstractions\Plugin.Badge.Abstractions.csproj">

0 commit comments

Comments
 (0)