Skip to content

Commit c470854

Browse files
authored
Code Quality: Implemented Breadcrumb (#16946)
1 parent 3a057b8 commit c470854

15 files changed

+986
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
using CommunityToolkit.WinUI;
5+
6+
namespace Files.App.Controls
7+
{
8+
public partial class BreadcrumbBar : Control
9+
{
10+
[GeneratedDependencyProperty]
11+
public partial FrameworkElement? RootItem { get; set; }
12+
13+
[GeneratedDependencyProperty]
14+
public partial object? ItemsSource { get; set; }
15+
16+
[GeneratedDependencyProperty]
17+
public partial object? ItemTemplate { get; set; }
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.UI.Xaml.Automation;
5+
using Windows.Foundation;
6+
7+
namespace Files.App.Controls
8+
{
9+
public partial class BreadcrumbBar : Control
10+
{
11+
// Constants
12+
13+
private const string TemplatePartName_RootBreadcrumbBarItem = "PART_RootBreadcrumbBarItem";
14+
private const string TemplatePartName_EllipsisBreadcrumbBarItem = "PART_EllipsisBreadcrumbBarItem";
15+
private const string TemplatePartName_MainItemsRepeater = "PART_MainItemsRepeater";
16+
17+
// Fields
18+
19+
private readonly BreadcrumbBarLayout _itemsRepeaterLayout;
20+
21+
private BreadcrumbBarItem? _rootBreadcrumbBarItem;
22+
private BreadcrumbBarItem? _ellipsisBreadcrumbBarItem;
23+
private BreadcrumbBarItem? _lastBreadcrumbBarItem;
24+
private ItemsRepeater? _itemsRepeater;
25+
26+
private bool _isEllipsisRendered;
27+
28+
// Properties
29+
30+
public int IndexAfterEllipsis
31+
=> _itemsRepeaterLayout.IndexAfterEllipsis;
32+
33+
// Events
34+
35+
public event TypedEventHandler<BreadcrumbBar, BreadcrumbBarItemClickedEventArgs>? ItemClicked;
36+
public event EventHandler<BreadcrumbBarItemDropDownFlyoutEventArgs>? ItemDropDownFlyoutOpening;
37+
public event EventHandler<BreadcrumbBarItemDropDownFlyoutEventArgs>? ItemDropDownFlyoutClosed;
38+
39+
// Constructor
40+
41+
public BreadcrumbBar()
42+
{
43+
DefaultStyleKey = typeof(BreadcrumbBar);
44+
45+
_itemsRepeaterLayout = new(this, 2d);
46+
}
47+
48+
// Methods
49+
50+
protected override void OnApplyTemplate()
51+
{
52+
base.OnApplyTemplate();
53+
54+
_rootBreadcrumbBarItem = GetTemplateChild(TemplatePartName_RootBreadcrumbBarItem) as BreadcrumbBarItem
55+
?? throw new MissingFieldException($"Could not find {TemplatePartName_RootBreadcrumbBarItem} in the given {nameof(BreadcrumbBar)}'s style.");
56+
_ellipsisBreadcrumbBarItem = GetTemplateChild(TemplatePartName_EllipsisBreadcrumbBarItem) as BreadcrumbBarItem
57+
?? throw new MissingFieldException($"Could not find {TemplatePartName_EllipsisBreadcrumbBarItem} in the given {nameof(BreadcrumbBar)}'s style.");
58+
_itemsRepeater = GetTemplateChild(TemplatePartName_MainItemsRepeater) as ItemsRepeater
59+
?? throw new MissingFieldException($"Could not find {TemplatePartName_MainItemsRepeater} in the given {nameof(BreadcrumbBar)}'s style.");
60+
61+
_rootBreadcrumbBarItem.SetOwner(this);
62+
_ellipsisBreadcrumbBarItem.SetOwner(this);
63+
_itemsRepeater.Layout = _itemsRepeaterLayout;
64+
65+
_itemsRepeater.ElementPrepared += ItemsRepeater_ElementPrepared;
66+
_itemsRepeater.ItemsSourceView.CollectionChanged += ItemsSourceView_CollectionChanged;
67+
}
68+
69+
internal protected virtual void RaiseItemClickedEvent(BreadcrumbBarItem item)
70+
{
71+
var index = _itemsRepeater?.GetElementIndex(item) ?? throw new ArgumentNullException($"{_itemsRepeater} is null.");
72+
var eventArgs = new BreadcrumbBarItemClickedEventArgs(item, index, item == _rootBreadcrumbBarItem);
73+
ItemClicked?.Invoke(this, eventArgs);
74+
}
75+
76+
internal protected virtual void RaiseItemDropDownFlyoutOpening(BreadcrumbBarItem item, MenuFlyout flyout)
77+
{
78+
var index = _itemsRepeater?.GetElementIndex(item) ?? throw new ArgumentNullException($"{_itemsRepeater} is null.");
79+
ItemDropDownFlyoutOpening?.Invoke(this, new(flyout, item, index, item == _rootBreadcrumbBarItem));
80+
}
81+
82+
internal protected virtual void RaiseItemDropDownFlyoutClosed(BreadcrumbBarItem item, MenuFlyout flyout)
83+
{
84+
var index = _itemsRepeater?.GetElementIndex(item) ?? throw new ArgumentNullException($"{_itemsRepeater} is null.");
85+
ItemDropDownFlyoutClosed?.Invoke(this, new(flyout, item, index, item == _rootBreadcrumbBarItem));
86+
}
87+
88+
internal protected virtual void OnLayoutUpdated()
89+
{
90+
if (_itemsRepeater is null)
91+
return;
92+
93+
_isEllipsisRendered = _itemsRepeaterLayout.EllipsisIsRendered;
94+
if (_ellipsisBreadcrumbBarItem is not null)
95+
_ellipsisBreadcrumbBarItem.Visibility = _isEllipsisRendered ? Visibility.Visible : Visibility.Collapsed;
96+
97+
for (int accessibilityIndex = 0, collectionIndex = _itemsRepeaterLayout.IndexAfterEllipsis;
98+
accessibilityIndex < _itemsRepeaterLayout.VisibleItemsCount;
99+
accessibilityIndex++, collectionIndex++)
100+
{
101+
if (_itemsRepeater.TryGetElement(collectionIndex) is { } element)
102+
{
103+
element.SetValue(AutomationProperties.PositionInSetProperty, accessibilityIndex);
104+
element.SetValue(AutomationProperties.SizeOfSetProperty, _itemsRepeaterLayout.VisibleItemsCount);
105+
}
106+
}
107+
}
108+
109+
internal bool TryGetElement(int index, out BreadcrumbBarItem? item)
110+
{
111+
item = null;
112+
113+
if (_itemsRepeater is null)
114+
return false;
115+
116+
item = _itemsRepeater.TryGetElement(index) as BreadcrumbBarItem;
117+
118+
return item is not null;
119+
}
120+
121+
// Event methods
122+
123+
private void ItemsRepeater_ElementPrepared(ItemsRepeater sender, ItemsRepeaterElementPreparedEventArgs args)
124+
{
125+
if (args.Element is not BreadcrumbBarItem item || _itemsRepeater is null)
126+
return;
127+
128+
if (args.Index == _itemsRepeater.ItemsSourceView.Count - 1)
129+
{
130+
_lastBreadcrumbBarItem = item;
131+
_lastBreadcrumbBarItem.IsLastItem = true;
132+
}
133+
134+
item.SetOwner(this);
135+
}
136+
137+
private void ItemsSourceView_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
138+
{
139+
if (_lastBreadcrumbBarItem is not null)
140+
_lastBreadcrumbBarItem.IsLastItem = false;
141+
142+
if (e.NewItems is not null &&
143+
e.NewItems.Count > 0 &&
144+
e.NewItems[e.NewItems.Count - 1] is BreadcrumbBarItem item)
145+
{
146+
_lastBreadcrumbBarItem = item;
147+
item.IsLastItem = true;
148+
}
149+
}
150+
}
151+
}

0 commit comments

Comments
 (0)