Skip to content

Commit fdd6a63

Browse files
committed
Implemented root element
1 parent 69abf35 commit fdd6a63

File tree

8 files changed

+273
-26
lines changed

8 files changed

+273
-26
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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+
12+
namespace Files.App.Controls
13+
{
14+
public partial class BreadcrumbBar
15+
{
16+
private void RootItemButton_PointerEntered(object sender, PointerRoutedEventArgs e)
17+
{
18+
VisualStateManager.GoToState(this, "PointerOverItem", true);
19+
}
20+
21+
private void RootItemButton_PointerPressed(object sender, PointerRoutedEventArgs e)
22+
{
23+
VisualStateManager.GoToState(this, "PointerPressedOnItem", true);
24+
}
25+
26+
private void RootItemButton_PointerReleased(object sender, PointerRoutedEventArgs e)
27+
{
28+
VisualStateManager.GoToState(this, "PointerOverItem", true);
29+
}
30+
31+
private void RootItemButton_PointerExited(object sender, PointerRoutedEventArgs e)
32+
{
33+
VisualStateManager.GoToState(this, "PointerNormal", true);
34+
}
35+
36+
private void RootChevronItemButton_PointerEntered(object sender, PointerRoutedEventArgs e)
37+
{
38+
VisualStateManager.GoToState(this, "PointerOverChevron", true);
39+
}
40+
41+
private void RootChevronItemButton_PointerPressed(object sender, PointerRoutedEventArgs e)
42+
{
43+
VisualStateManager.GoToState(this, "PointerPressedOnChevron", true);
44+
}
45+
46+
private void RootChevronItemButton_PointerReleased(object sender, PointerRoutedEventArgs e)
47+
{
48+
VisualStateManager.GoToState(this, "PointerOverChevron", true);
49+
50+
FlyoutBase.ShowAttachedFlyout(_rootItemChevronButton);
51+
}
52+
53+
private void RootChevronItemButton_PointerExited(object sender, PointerRoutedEventArgs e)
54+
{
55+
VisualStateManager.GoToState(this, "PointerNormal", true);
56+
}
57+
58+
private void RootItemChevronDropDownMenuFlyout_Opening(object? sender, object e)
59+
{
60+
RootItemDropDownFlyoutOpening?.Invoke(this, sender as MenuFlyout);
61+
}
62+
63+
private void RootItemChevronDropDownMenuFlyout_Opened(object? sender, object e)
64+
{
65+
VisualStateManager.GoToState(this, "ChevronNormalOn", true);
66+
}
67+
68+
private void RootItemChevronDropDownMenuFlyout_Closed(object? sender, object e)
69+
{
70+
RootItemDropDownFlyoutOpening?.Invoke(this, sender as MenuFlyout);
71+
72+
VisualStateManager.GoToState(this, "ChevronNormalOff", true);
73+
VisualStateManager.GoToState(this, "PointerNormal", true);
74+
}
75+
}
76+
}

src/Files.App.Controls/BreadcrumbBar/BreadcrumbBar.Properties.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ public partial class BreadcrumbBar : Control
2020
[GeneratedDependencyProperty]
2121
public partial object? ItemTemplate { get; set; }
2222

23+
[GeneratedDependencyProperty]
24+
public partial FrameworkElement? RootElement { get; set; }
25+
2326
partial void OnItemsSourceChanged(object? newValue)
2427
{
2528
}

src/Files.App.Controls/BreadcrumbBar/BreadcrumbBar.cs

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,30 @@ public partial class BreadcrumbBar : Control
2020
{
2121
// Constants
2222

23-
private const string _itemsRepeaterName = "PART_RootItemsRepeater";
23+
private const string TemplatePartName_RootItemButton = "PART_RootItemButton";
24+
private const string TemplatePartName_RootItemChevronButton = "PART_RootItemChevronButton";
25+
private const string TemplatePartName_RootElementChevronDropDownMenuFlyout = "PART_RootElementChevronDropDownMenuFlyout";
26+
private const string TemplatePartName_MainItemsRepeater = "PART_MainItemsRepeater";
2427

2528
// Fields
2629

27-
private ItemsRepeater _itemsRepeater = null!;
2830
private BreadcrumbBarLayout _itemsRepeaterLayout = null!;
2931

32+
private Border? _rootItemButton;
33+
private Border? _rootItemChevronButton;
34+
private MenuFlyout? _rootItemChevronDropDownMenuFlyout;
35+
private ItemsRepeater? _itemsRepeater;
36+
3037
// Events
3138

3239
public event TypedEventHandler<BreadcrumbBar, BreadcrumbBarItemClickedEventArgs>? ItemClicked;
3340
public event EventHandler<BreadcrumbBarItemDropDownFlyoutEventArgs>? ItemDropDownFlyoutOpening;
3441
public event EventHandler<BreadcrumbBarItemDropDownFlyoutEventArgs>? ItemDropDownFlyoutClosed;
3542

43+
public event EventHandler? RootItemClicked;
44+
public event EventHandler<MenuFlyout>? RootItemDropDownFlyoutOpening;
45+
public event EventHandler<MenuFlyout>? RootItemDropDownFlyoutClosed;
46+
3647
// Constructor
3748

3849
public BreadcrumbBar()
@@ -46,32 +57,52 @@ public BreadcrumbBar()
4657

4758
protected override void OnApplyTemplate()
4859
{
49-
base.OnApplyTemplate();
50-
51-
_itemsRepeater = GetTemplateChild(_itemsRepeaterName) as ItemsRepeater
52-
?? throw new MissingFieldException($"Could not find {_itemsRepeaterName} in the given {nameof(BreadcrumbBar)}'s style.");
60+
_rootItemButton = GetTemplateChild(TemplatePartName_RootItemButton) as Border
61+
?? throw new MissingFieldException($"Could not find {TemplatePartName_RootItemButton} in the given {nameof(BreadcrumbBar)}'s style.");
62+
_rootItemChevronButton = GetTemplateChild(TemplatePartName_RootItemChevronButton) as Border
63+
?? throw new MissingFieldException($"Could not find {TemplatePartName_RootItemChevronButton} in the given {nameof(BreadcrumbBar)}'s style.");
64+
_rootItemChevronDropDownMenuFlyout = GetTemplateChild(TemplatePartName_RootElementChevronDropDownMenuFlyout) as MenuFlyout
65+
?? throw new MissingFieldException($"Could not find {TemplatePartName_RootElementChevronDropDownMenuFlyout} in the given {nameof(BreadcrumbBar)}'s style.");
66+
_itemsRepeater = GetTemplateChild(TemplatePartName_MainItemsRepeater) as ItemsRepeater
67+
?? throw new MissingFieldException($"Could not find {TemplatePartName_MainItemsRepeater} in the given {nameof(BreadcrumbBar)}'s style.");
5368

5469
//_itemsRepeater.Layout = _itemsRepeaterLayout;
5570

71+
_rootItemButton.PointerEntered += RootItemButton_PointerEntered;
72+
_rootItemButton.PointerPressed += RootItemButton_PointerPressed;
73+
_rootItemButton.PointerReleased += RootItemButton_PointerReleased;
74+
_rootItemButton.PointerExited += RootItemButton_PointerExited;
75+
76+
_rootItemChevronButton.PointerEntered += RootChevronItemButton_PointerEntered;
77+
_rootItemChevronButton.PointerPressed += RootChevronItemButton_PointerPressed;
78+
_rootItemChevronButton.PointerReleased += RootChevronItemButton_PointerReleased;
79+
_rootItemChevronButton.PointerExited += RootChevronItemButton_PointerExited;
80+
81+
_rootItemChevronDropDownMenuFlyout.Opening += RootItemChevronDropDownMenuFlyout_Opening;
82+
_rootItemChevronDropDownMenuFlyout.Opened += RootItemChevronDropDownMenuFlyout_Opened;
83+
_rootItemChevronDropDownMenuFlyout.Closed += RootItemChevronDropDownMenuFlyout_Closed;
84+
5685
_itemsRepeater.ElementPrepared += ItemsRepeater_ElementPrepared;
86+
87+
base.OnApplyTemplate();
5788
}
5889

5990
internal void RaiseItemClickedEvent(BreadcrumbBarItem item)
6091
{
61-
var index = _itemsRepeater.GetElementIndex(item);
92+
var index = _itemsRepeater?.GetElementIndex(item) ?? throw new ArgumentNullException($"{_itemsRepeater} is null.");
6293
var eventArgs = new BreadcrumbBarItemClickedEventArgs(item, index);
6394
ItemClicked?.Invoke(this, eventArgs);
6495
}
6596

6697
internal void RaiseItemDropDownFlyoutOpening(BreadcrumbBarItem item, MenuFlyout flyout)
6798
{
68-
var index = _itemsRepeater.GetElementIndex(item);
99+
var index = _itemsRepeater?.GetElementIndex(item) ?? throw new ArgumentNullException($"{_itemsRepeater} is null.");
69100
ItemDropDownFlyoutOpening?.Invoke(this, new(item, index, flyout));
70101
}
71102

72103
internal void RaiseItemDropDownFlyoutClosed(BreadcrumbBarItem item, MenuFlyout flyout)
73104
{
74-
var index = _itemsRepeater.GetElementIndex(item);
105+
var index = _itemsRepeater?.GetElementIndex(item) ?? throw new ArgumentNullException($"{_itemsRepeater} is null.");
75106
ItemDropDownFlyoutClosed?.Invoke(this, new(item, index, flyout));
76107
}
77108

src/Files.App.Controls/BreadcrumbBar/BreadcrumbBar.xaml

Lines changed: 120 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
xmlns:local="using:Files.App.Controls">
77

88
<x:Double x:Key="BreadcrumbBarItemHeight">32</x:Double>
9+
<CornerRadius x:Key="BreadcrumbBarRootItemCornerRadius">16,2,2,16</CornerRadius>
910

1011
<Style BasedOn="{StaticResource DefaultBreadcrumbBarStyle}" TargetType="local:BreadcrumbBar" />
1112
<Style BasedOn="{StaticResource DefaultBreadcrumbBarItemStyle}" TargetType="local:BreadcrumbBarItem" />
@@ -16,14 +17,125 @@
1617
<Setter Property="Template">
1718
<Setter.Value>
1819
<ControlTemplate TargetType="local:BreadcrumbBar">
19-
<ItemsRepeater
20-
x:Name="PART_RootItemsRepeater"
21-
ItemTemplate="{Binding ItemTemplate, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
22-
ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}">
23-
<ItemsRepeater.Layout>
24-
<StackLayout Orientation="Horizontal" Spacing="2" />
25-
</ItemsRepeater.Layout>
26-
</ItemsRepeater>
20+
<Grid ColumnSpacing="2">
21+
<Grid.ColumnDefinitions>
22+
<ColumnDefinition Width="Auto" />
23+
<ColumnDefinition Width="Auto" />
24+
<ColumnDefinition Width="*" />
25+
</Grid.ColumnDefinitions>
26+
27+
<Border
28+
x:Name="PART_RootItemButton"
29+
Grid.Column="0"
30+
Padding="16,0,8,0"
31+
Background="Transparent"
32+
CornerRadius="{StaticResource BreadcrumbBarRootItemCornerRadius}">
33+
<Border.BackgroundTransition>
34+
<BrushTransition Duration="0:0:0.083" />
35+
</Border.BackgroundTransition>
36+
37+
<ContentPresenter Content="{TemplateBinding RootElement}" />
38+
</Border>
39+
40+
<Border
41+
x:Name="PART_RootItemChevronButton"
42+
Grid.Column="1"
43+
Background="Transparent"
44+
CornerRadius="2">
45+
<Border.BackgroundTransition>
46+
<BrushTransition Duration="0:0:0.083" />
47+
</Border.BackgroundTransition>
48+
<FlyoutBase.AttachedFlyout>
49+
<MenuFlyout
50+
x:Name="PART_RootElementChevronDropDownMenuFlyout"
51+
Placement="BottomEdgeAlignedLeft"
52+
ScrollViewer.VerticalScrollBarVisibility="Auto"
53+
ScrollViewer.VerticalScrollMode="Auto">
54+
<MenuFlyout.MenuFlyoutPresenterStyle>
55+
<Style TargetType="MenuFlyoutPresenter">
56+
<Setter Property="MaxHeight" Value="400" />
57+
<!-- Workaround for https://github.com/files-community/Files/issues/13078 -->
58+
<Setter Target="HighContrastAdjustment" Value="None" />
59+
</Style>
60+
</MenuFlyout.MenuFlyoutPresenterStyle>
61+
</MenuFlyout>
62+
</FlyoutBase.AttachedFlyout>
63+
64+
<AnimatedIcon
65+
x:Name="PART_ChevronAnimatedIcon"
66+
Width="12"
67+
Height="12"
68+
Margin="4,0"
69+
HorizontalAlignment="Center"
70+
VerticalAlignment="Center"
71+
AnimatedIcon.State="NormalOff"
72+
MirroredWhenRightToLeft="True"
73+
RenderTransformOrigin="0.5, 0.5">
74+
<AnimatedIcon.FallbackIconSource>
75+
<FontIconSource
76+
x:Name="ChevronAnimatedIconFallbackFontIcon"
77+
FontSize="12"
78+
Glyph="&#xE76C;"
79+
IsTextScaleFactorEnabled="False" />
80+
</AnimatedIcon.FallbackIconSource>
81+
82+
<animatedvisuals:AnimatedChevronRightDownSmallVisualSource />
83+
84+
</AnimatedIcon>
85+
</Border>
86+
87+
<ItemsRepeater
88+
x:Name="PART_MainItemsRepeater"
89+
Grid.Column="2"
90+
ItemTemplate="{Binding ItemTemplate, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
91+
ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}">
92+
<ItemsRepeater.Layout>
93+
<StackLayout Orientation="Horizontal" Spacing="2" />
94+
</ItemsRepeater.Layout>
95+
</ItemsRepeater>
96+
97+
<VisualStateManager.VisualStateGroups>
98+
99+
<VisualStateGroup x:Name="PointerStates">
100+
<VisualState x:Name="PointerNormal" />
101+
102+
<VisualState x:Name="PointerOverItem">
103+
<VisualState.Setters>
104+
<Setter Target="PART_RootItemButton.Background" Value="{ThemeResource ControlFillColorSecondaryBrush}" />
105+
<Setter Target="PART_RootItemChevronButton.Background" Value="{ThemeResource ControlFillColorSecondaryBrush}" />
106+
</VisualState.Setters>
107+
</VisualState>
108+
<VisualState x:Name="PointerOverChevron">
109+
<VisualState.Setters>
110+
<Setter Target="PART_RootItemChevronButton.Background" Value="{ThemeResource ControlFillColorSecondaryBrush}" />
111+
</VisualState.Setters>
112+
</VisualState>
113+
114+
<VisualState x:Name="PointerPressedOnItem">
115+
<VisualState.Setters>
116+
<Setter Target="PART_RootItemButton.Background" Value="{ThemeResource ControlFillColorTertiaryBrush}" />
117+
<Setter Target="PART_RootItemChevronButton.Background" Value="{ThemeResource ControlFillColorTertiaryBrush}" />
118+
</VisualState.Setters>
119+
</VisualState>
120+
<VisualState x:Name="PointerPressedOnChevron">
121+
<VisualState.Setters>
122+
<Setter Target="PART_RootItemChevronButton.Background" Value="{ThemeResource ControlFillColorTertiaryBrush}" />
123+
</VisualState.Setters>
124+
</VisualState>
125+
126+
</VisualStateGroup>
127+
128+
<VisualStateGroup x:Name="ChevronStates">
129+
<VisualState x:Name="ChevronNormalOff" />
130+
<VisualState x:Name="ChevronNormalOn">
131+
<VisualState.Setters>
132+
<Setter Target="PART_ChevronAnimatedIcon.(AnimatedIcon.State)" Value="NormalOn" />
133+
</VisualState.Setters>
134+
</VisualState>
135+
</VisualStateGroup>
136+
137+
</VisualStateManager.VisualStateGroups>
138+
</Grid>
27139
</ControlTemplate>
28140
</Setter.Value>
29141
</Setter>

src/Files.App.Controls/BreadcrumbBar/BreadcrumbBarItem.Events.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
namespace Files.App.Controls
1313
{
14-
public partial class BreadcrumbBarItem : ContentControl
14+
public partial class BreadcrumbBarItem
1515
{
1616
private void ItemButton_PointerEntered(object sender, PointerRoutedEventArgs e)
1717
{
@@ -50,7 +50,7 @@ private void ChevronButton_PointerReleased(object sender, PointerRoutedEventArgs
5050
{
5151
VisualStateManager.GoToState(this, "PointerOverChevron", true);
5252

53-
TryOpenFlyout();
53+
FlyoutBase.ShowAttachedFlyout(_chevronButton);
5454
}
5555

5656
private void ChevronButton_PointerExited(object sender, PointerRoutedEventArgs e)

src/Files.App.Controls/BreadcrumbBar/BreadcrumbBarItem.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,5 @@ internal void SetOwner(BreadcrumbBar breadcrumbBar)
6666
{
6767
_ownerRef = new(breadcrumbBar);
6868
}
69-
70-
private void TryOpenFlyout()
71-
{
72-
FlyoutBase.ShowAttachedFlyout(_chevronButton);
73-
}
7469
}
7570
}

tests/Files.App.UITests/Views/BreadcrumbBarPage.xaml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<controls:SamplePanel.MainContent>
1717
<Grid
1818
Height="36"
19-
Padding="16,2"
19+
Padding="2"
2020
HorizontalAlignment="Stretch"
2121
VerticalAlignment="Center"
2222
Background="{ThemeResource ControlFillColorDefaultBrush}"
@@ -28,7 +28,17 @@
2828
ItemClicked="BreadcrumbBar1_ItemClicked"
2929
ItemDropDownFlyoutClosed="BreadcrumbBar1_ItemDropDownFlyoutClosed"
3030
ItemDropDownFlyoutOpening="BreadcrumbBar1_ItemDropDownFlyoutOpening"
31-
ItemsSource="{x:Bind DummyItems, Mode=OneWay}">
31+
ItemsSource="{x:Bind DummyItems, Mode=OneWay}"
32+
RootItemClicked="BreadcrumbBar1_RootItemClicked"
33+
RootItemDropDownFlyoutClosed="BreadcrumbBar1_RootItemDropDownFlyoutClosed"
34+
RootItemDropDownFlyoutOpening="BreadcrumbBar1_RootItemDropDownFlyoutOpening">
35+
<controls:BreadcrumbBar.RootElement>
36+
<Image
37+
Width="16"
38+
Height="16"
39+
VerticalAlignment="Center"
40+
Source="/Data/DummyIcon1.png" />
41+
</controls:BreadcrumbBar.RootElement>
3242
<controls:BreadcrumbBar.ItemTemplate>
3343
<DataTemplate x:DataType="data:DummyItem2">
3444
<controls:BreadcrumbBarItem Content="{x:Bind}">

0 commit comments

Comments
 (0)