Skip to content

Commit 383d82c

Browse files
0x5bfamdtaukyaira2
authored
Code Quality: Implemented Omnibar (#16910)
Signed-off-by: 0x5BFA <[email protected]> Co-authored-by: Martin Anderson <[email protected]> Co-authored-by: Yair <[email protected]>
1 parent 2ea3dfe commit 383d82c

27 files changed

+2514
-1859
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
using CommunityToolkit.WinUI;
5+
using Microsoft.UI.Xaml;
6+
using Microsoft.UI.Xaml.Controls;
7+
using Microsoft.UI.Xaml.Media;
8+
using Microsoft.UI.Xaml.Markup;
9+
using Microsoft.UI.Xaml.Shapes;
10+
using System.Linq;
11+
using System.Collections.Generic;
12+
13+
namespace Files.App.Controls
14+
{
15+
public partial class Omnibar
16+
{
17+
[GeneratedDependencyProperty]
18+
public partial IList<OmnibarMode>? Modes { get; set; }
19+
20+
[GeneratedDependencyProperty]
21+
public partial OmnibarMode? CurrentSelectedMode { get; set; }
22+
}
23+
}
+180
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
using CommunityToolkit.WinUI;
5+
using Microsoft.UI.Xaml;
6+
using Microsoft.UI.Xaml.Controls;
7+
using Microsoft.UI.Xaml.Media;
8+
using Microsoft.UI.Xaml.Markup;
9+
using Microsoft.UI.Xaml.Shapes;
10+
using Microsoft.UI.Xaml.Input;
11+
using Microsoft.UI;
12+
using Windows.ApplicationModel.Contacts;
13+
14+
namespace Files.App.Controls
15+
{
16+
// Content
17+
[ContentProperty(Name = nameof(Modes))]
18+
// Template parts
19+
[TemplatePart(Name = "PART_ModesHostGrid", Type = typeof(Grid))]
20+
// Visual states
21+
[TemplateVisualState(Name = "Focused", GroupName = "FocusStates")]
22+
[TemplateVisualState(Name = "Normal", GroupName = "FocusStates")]
23+
public partial class Omnibar : Control
24+
{
25+
private const string ModesHostGrid = "PART_ModesHostGrid";
26+
private const string AutoSuggestPopup = "PART_AutoSuggestPopup";
27+
private const string AutoSuggestBoxBorder = "PART_AutoSuggestBoxBorder";
28+
29+
private Grid? _modesHostGrid;
30+
private Popup? _autoSuggestPopup;
31+
private Border? _autoSuggestBoxBorder;
32+
private bool _isFocused;
33+
private bool _stillHasFocus;
34+
35+
public Omnibar()
36+
{
37+
DefaultStyleKey = typeof(Omnibar);
38+
39+
Modes ??= [];
40+
}
41+
42+
protected override void OnApplyTemplate()
43+
{
44+
_modesHostGrid = GetTemplateChild(ModesHostGrid) as Grid
45+
?? throw new MissingFieldException($"Could not find {ModesHostGrid} in the given {nameof(Omnibar)}'s style.");
46+
_autoSuggestPopup = GetTemplateChild(AutoSuggestPopup) as Popup
47+
?? throw new MissingFieldException($"Could not find {AutoSuggestPopup} in the given {nameof(Omnibar)}'s style.");
48+
_autoSuggestBoxBorder = GetTemplateChild(AutoSuggestBoxBorder) as Border
49+
?? throw new MissingFieldException($"Could not find {AutoSuggestBoxBorder} in the given {nameof(Omnibar)}'s style.");
50+
51+
if (Modes is null)
52+
return;
53+
54+
// Add shadow to the popup and set the proper width
55+
_autoSuggestBoxBorder!.Translation = new(0, 0, 32);
56+
_autoSuggestBoxBorder!.Width = _modesHostGrid!.ActualWidth;
57+
58+
// Populate the modes
59+
foreach (var mode in Modes)
60+
{
61+
// Insert a divider
62+
if (_modesHostGrid.Children.Count is not 0)
63+
{
64+
var divider = new Rectangle()
65+
{
66+
Fill = (SolidColorBrush)Application.Current.Resources["DividerStrokeColorDefaultBrush"],
67+
Height = 20,
68+
Margin = new(2,0,2,0),
69+
Width = 1,
70+
};
71+
72+
_modesHostGrid.ColumnDefinitions.Add(new() { Width = GridLength.Auto });
73+
Grid.SetColumn(divider, _modesHostGrid.Children.Count);
74+
_modesHostGrid.Children.Add(divider);
75+
}
76+
77+
// Insert the mode
78+
_modesHostGrid.ColumnDefinitions.Add(new() { Width = GridLength.Auto });
79+
Grid.SetColumn(mode, _modesHostGrid.Children.Count);
80+
_modesHostGrid.Children.Add(mode);
81+
mode.Host = this;
82+
}
83+
84+
_modesHostGrid.SizeChanged += ModesHostGrid_SizeChanged;
85+
86+
GotFocus += Omnibar_GotFocus;
87+
LostFocus += Omnibar_LostFocus;
88+
LosingFocus += Omnibar_LosingFocus;
89+
90+
UpdateVisualStates();
91+
92+
base.OnApplyTemplate();
93+
}
94+
95+
// Methods
96+
97+
internal void ChangeMode(OmnibarMode modeToExpand)
98+
{
99+
if (_modesHostGrid is null || Modes is null)
100+
throw new NullReferenceException();
101+
102+
// Reset
103+
foreach (var column in _modesHostGrid.ColumnDefinitions)
104+
column.Width = GridLength.Auto;
105+
foreach (var mode in Modes)
106+
VisualStateManager.GoToState(mode, "Unfocused", true);
107+
108+
// Expand the given mode
109+
VisualStateManager.GoToState(modeToExpand, "Focused", true);
110+
_modesHostGrid.ColumnDefinitions[_modesHostGrid.Children.IndexOf(modeToExpand)].Width = new(1, GridUnitType.Star);
111+
112+
CurrentSelectedMode = modeToExpand;
113+
114+
UpdateVisualStates();
115+
}
116+
117+
private void UpdateVisualStates()
118+
{
119+
VisualStateManager.GoToState(this, _isFocused ? "Focused" : "Normal", true);
120+
121+
if (CurrentSelectedMode is not null && _autoSuggestPopup is not null)
122+
{
123+
// Close anyway
124+
if (_autoSuggestPopup.IsOpen && CurrentSelectedMode.SuggestionItemsSource is null)
125+
VisualStateManager.GoToState(this, "PopupClosed", true);
126+
127+
// Decide open or close
128+
if (_isFocused != _autoSuggestPopup.IsOpen)
129+
VisualStateManager.GoToState(this, _isFocused && CurrentSelectedMode.SuggestionItemsSource is not null ? "PopupOpened" : "PopupClosed", true);
130+
}
131+
132+
if (CurrentSelectedMode is not null)
133+
VisualStateManager.GoToState(
134+
CurrentSelectedMode,
135+
_isFocused
136+
? "Focused"
137+
: CurrentSelectedMode.ContentOnInactive is null
138+
? "CurrentUnfocusedWithoutInactiveMode"
139+
: "CurrentUnfocusedWithInactiveMode",
140+
true);
141+
}
142+
143+
// Events
144+
145+
private void ModesHostGrid_SizeChanged(object sender, SizeChangedEventArgs e)
146+
{
147+
_autoSuggestBoxBorder!.Width = _modesHostGrid!.ActualWidth;
148+
}
149+
150+
private void Omnibar_GotFocus(object sender, RoutedEventArgs e)
151+
{
152+
_isFocused = true;
153+
UpdateVisualStates();
154+
}
155+
156+
private void Omnibar_LosingFocus(UIElement sender, LosingFocusEventArgs args)
157+
{
158+
// Ignore when user clicks on the TextBox or the button area of an OmnibarMode, Omnibar still has focus anyway
159+
if (args.NewFocusedElement?.GetType() is not { } focusedType ||
160+
focusedType == typeof(TextBox) ||
161+
focusedType == typeof(OmnibarMode) ||
162+
focusedType == typeof(Omnibar))
163+
{
164+
_stillHasFocus = true;
165+
}
166+
}
167+
168+
private void Omnibar_LostFocus(object sender, RoutedEventArgs e)
169+
{
170+
if (_stillHasFocus)
171+
{
172+
_stillHasFocus = false;
173+
return;
174+
}
175+
176+
_isFocused = false;
177+
UpdateVisualStates();
178+
}
179+
}
180+
}
+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<!-- Copyright (c) Files Community. Licensed under the MIT License. -->
2+
<ResourceDictionary
3+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
4+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
5+
xmlns:local="using:Files.App.Controls">
6+
7+
<!--<ResourceDictionary.ThemeDictionaries>
8+
<ResourceDictionary x:Key="Default">
9+
<SolidColorBrush x:Key="OmnibarModeDivisiderBrush" Color="{ThemeResource DividerStrokeColorDefaultBrush}" />
10+
</ResourceDictionary>
11+
<ResourceDictionary x:Key="HighContrast">
12+
<SolidColorBrush x:Key="OmnibarModeDivisiderBrush" Color="{ThemeResource DividerStrokeColorDefaultBrush}" />
13+
</ResourceDictionary>
14+
</ResourceDictionary.ThemeDictionaries>-->
15+
16+
<x:Double x:Key="OmnibarDefaultHeight">38</x:Double>
17+
<CornerRadius x:Key="OmnibarDefaultCornerRadius">19</CornerRadius>
18+
<Thickness x:Key="OmnibarFocusedBorderThickness">2</Thickness>
19+
<Thickness x:Key="OmnibarUnfocusedBorderThickness">1</Thickness>
20+
<Thickness x:Key="OmnibarUnfocusedRootPadding">1</Thickness>
21+
22+
<Style BasedOn="{StaticResource DefaultOmnibarStyle}" TargetType="local:Omnibar" />
23+
24+
<Style x:Key="DefaultOmnibarStyle" TargetType="local:Omnibar">
25+
<Setter Property="IsTabStop" Value="True" />
26+
<Setter Property="UseSystemFocusVisuals" Value="True" />
27+
<Setter Property="HorizontalAlignment" Value="Stretch" />
28+
<Setter Property="Background" Value="{ThemeResource ControlFillColorDefaultBrush}" />
29+
<Setter Property="Padding" Value="{StaticResource OmnibarUnfocusedRootPadding}" />
30+
<Setter Property="BorderBrush" Value="{ThemeResource CircleElevationBorderBrush}" />
31+
<Setter Property="BorderThickness" Value="{StaticResource OmnibarUnfocusedBorderThickness}" />
32+
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
33+
<Setter Property="CornerRadius" Value="{StaticResource OmnibarDefaultCornerRadius}" />
34+
<Setter Property="VerticalAlignment" Value="Center" />
35+
<Setter Property="IsFocusEngagementEnabled" Value="True" />
36+
<Setter Property="Template">
37+
<Setter.Value>
38+
<ControlTemplate TargetType="local:Omnibar">
39+
<Grid x:Name="PART_RootGrid">
40+
<Grid.RowDefinitions>
41+
<RowDefinition Height="Auto" />
42+
<RowDefinition Height="Auto" />
43+
</Grid.RowDefinitions>
44+
<!-- Input area -->
45+
<Grid
46+
x:Name="PART_ModesHostGrid"
47+
Grid.Row="0"
48+
Height="{StaticResource OmnibarDefaultHeight}"
49+
Padding="{TemplateBinding Padding}"
50+
Background="{TemplateBinding Background}"
51+
BorderBrush="{TemplateBinding BorderBrush}"
52+
BorderThickness="{TemplateBinding BorderThickness}"
53+
CornerRadius="{TemplateBinding CornerRadius}" />
54+
55+
<!-- Auto-suggest box area -->
56+
<Popup
57+
x:Name="PART_AutoSuggestPopup"
58+
Grid.Row="1"
59+
HorizontalAlignment="Stretch"
60+
IsOpen="False"
61+
ShouldConstrainToRootBounds="False">
62+
63+
<Border
64+
x:Name="PART_AutoSuggestBoxBorder"
65+
MaxHeight="200"
66+
Background="{ThemeResource AcrylicBackgroundFillColorDefaultBrush}"
67+
BorderBrush="{ThemeResource SurfaceStrokeColorFlyoutBrush}"
68+
BorderThickness="1"
69+
CornerRadius="{ThemeResource OverlayCornerRadius}">
70+
<Border.Shadow>
71+
<ThemeShadow />
72+
</Border.Shadow>
73+
74+
<ListView
75+
Padding="0,2"
76+
HorizontalAlignment="Stretch"
77+
IsItemClickEnabled="True"
78+
ItemTemplate="{Binding CurrentSelectedMode.SuggestionItemTemplate, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
79+
ItemsSource="{Binding CurrentSelectedMode.SuggestionItemsSource, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
80+
SelectionMode="None" />
81+
82+
</Border>
83+
</Popup>
84+
85+
<VisualStateManager.VisualStateGroups>
86+
87+
<VisualStateGroup x:Name="PointerStates">
88+
<VisualState x:Name="Normal" />
89+
<VisualState x:Name="Focused">
90+
<VisualState.Setters>
91+
<Setter Target="PART_ModesHostGrid.Margin" Value="-1" />
92+
<Setter Target="PART_ModesHostGrid.BorderThickness" Value="{StaticResource OmnibarFocusedBorderThickness}" />
93+
<Setter Target="PART_ModesHostGrid.BorderBrush" Value="{ThemeResource AccentFillColorDefaultBrush}" />
94+
</VisualState.Setters>
95+
</VisualState>
96+
</VisualStateGroup>
97+
98+
<VisualStateGroup x:Name="PopupVisibilityStates">
99+
<VisualState x:Name="PopupClosed" />
100+
<VisualState x:Name="PopupOpened">
101+
<VisualState.Setters>
102+
<Setter Target="PART_AutoSuggestPopup.IsOpen" Value="True" />
103+
</VisualState.Setters>
104+
</VisualState>
105+
</VisualStateGroup>
106+
107+
</VisualStateManager.VisualStateGroups>
108+
</Grid>
109+
</ControlTemplate>
110+
</Setter.Value>
111+
</Setter>
112+
</Style>
113+
114+
</ResourceDictionary>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
using CommunityToolkit.WinUI;
5+
using Microsoft.UI.Xaml;
6+
using Microsoft.UI.Xaml.Controls;
7+
using Microsoft.UI.Xaml.Media;
8+
using Microsoft.UI.Xaml.Markup;
9+
using Microsoft.UI.Xaml.Shapes;
10+
using System.Linq;
11+
using System.Collections.Generic;
12+
13+
namespace Files.App.Controls
14+
{
15+
public partial class OmnibarMode
16+
{
17+
[GeneratedDependencyProperty]
18+
public partial string? Text { get; set; }
19+
20+
[GeneratedDependencyProperty]
21+
public partial string? TextPlaceholder { get; set; }
22+
23+
[GeneratedDependencyProperty]
24+
public partial string? ModeName { get; set; }
25+
26+
[GeneratedDependencyProperty]
27+
public partial FrameworkElement? ContentOnInactive { get; set; }
28+
29+
[GeneratedDependencyProperty]
30+
public partial FrameworkElement? IconOnActive { get; set; }
31+
32+
[GeneratedDependencyProperty]
33+
public partial FrameworkElement? IconOnInactive { get; set; }
34+
35+
[GeneratedDependencyProperty]
36+
public partial object? SuggestionItemsSource { get; set; }
37+
38+
[GeneratedDependencyProperty]
39+
public partial DataTemplate? SuggestionItemTemplate { get; set; }
40+
41+
[GeneratedDependencyProperty]
42+
public partial bool IsDefault { get; set; }
43+
44+
[GeneratedDependencyProperty]
45+
internal partial Omnibar? Host { get; set; }
46+
}
47+
}

0 commit comments

Comments
 (0)