Skip to content

Commit c21a8f3

Browse files
committed
Initial commit
1 parent 79b7621 commit c21a8f3

15 files changed

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

0 commit comments

Comments
 (0)