From 2d3138bf60e47a467a88c8ca3a342f039242af65 Mon Sep 17 00:00:00 2001 From: heftymouse <59918974+heftymouse@users.noreply.github.com> Date: Thu, 6 Jun 2024 03:50:38 +0530 Subject: [PATCH 01/11] composition progress graph --- src/Files.App/Files.App.csproj | 5 +- .../UserControls/AddressToolbar.xaml | 3 +- .../UserControls/StatusCenter/SpeedGraph.xaml | 15 ++ .../StatusCenter/SpeedGraph.xaml.cs | 126 ++++++++++++++ .../{ => StatusCenter}/StatusCenter.xaml | 30 +--- .../{ => StatusCenter}/StatusCenter.xaml.cs | 2 +- .../Utils/StatusCenter/StatusCenterItem.cs | 157 ++---------------- 7 files changed, 161 insertions(+), 177 deletions(-) create mode 100644 src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml create mode 100644 src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml.cs rename src/Files.App/UserControls/{ => StatusCenter}/StatusCenter.xaml (92%) rename src/Files.App/UserControls/{ => StatusCenter}/StatusCenter.xaml.cs (96%) diff --git a/src/Files.App/Files.App.csproj b/src/Files.App/Files.App.csproj index e181346644b6..7ebcd2ab9298 100644 --- a/src/Files.App/Files.App.csproj +++ b/src/Files.App/Files.App.csproj @@ -39,7 +39,7 @@ TRACE;RELEASE;NETFX_CORE;DISABLE_XAML_GENERATED_MAIN true - + @@ -74,7 +74,6 @@ - @@ -140,5 +139,5 @@ - + diff --git a/src/Files.App/UserControls/AddressToolbar.xaml b/src/Files.App/UserControls/AddressToolbar.xaml index c7abdff6b60d..c7cfd048a082 100644 --- a/src/Files.App/UserControls/AddressToolbar.xaml +++ b/src/Files.App/UserControls/AddressToolbar.xaml @@ -16,6 +16,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:triggers="using:CommunityToolkit.WinUI.UI.Triggers" xmlns:uc="using:Files.App.UserControls" + xmlns:ucs="using:Files.App.UserControls.StatusCenter" x:Name="NavToolbar" Height="50" d:DesignHeight="50" @@ -481,7 +482,7 @@ - + + + + diff --git a/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml.cs b/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml.cs new file mode 100644 index 000000000000..dd435af96fc4 --- /dev/null +++ b/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml.cs @@ -0,0 +1,126 @@ +using Microsoft.Graphics.Canvas.Geometry; +using Microsoft.UI; +using Microsoft.UI.Composition; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Hosting; +using System.Collections.Specialized; +using System.Numerics; +using Windows.UI; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace Files.App.UserControls.StatusCenter +{ + public sealed partial class SpeedGraph : UserControl + { + public ObservableCollection Points + { + get => (ObservableCollection)GetValue(PointsProperty); + set + { + highestValue = 0; + SetValue(PointsProperty, value); + } + } + + public static readonly DependencyProperty PointsProperty = + DependencyProperty.Register(nameof(Points), typeof(ObservableCollection), typeof(SpeedGraph), null); + + Compositor compositor; + ContainerVisual rootVisual; + ShapeVisual graphVisual; + CompositionSpriteShape graphShape; + InsetClip graphClip; + + float width; + float height; + + float highestValue; + + public SpeedGraph() + { + this.InitializeComponent(); + } + + private void UserControl_Loaded(object sender, RoutedEventArgs e) + { + // is it a bad idea to recreate these on every load? + // TODO: it doesn't work the first time you open the flyout + rootVisual = (ContainerVisual)ElementCompositionPreview.GetElementVisual(graphRoot); + compositor = rootVisual.Compositor; + + width = rootVisual.Size.X; + height = rootVisual.Size.Y; + + var bgVisual = compositor.CreateSpriteVisual(); + bgVisual.Size = rootVisual.Size; + bgVisual.Brush = compositor.CreateColorBrush(Color.FromArgb(0x20, 0x40, 0xE0, 0xD0)); + rootVisual.Children.InsertAtBottom(bgVisual); + + graphVisual = compositor.CreateShapeVisual(); + graphVisual.Size = rootVisual.Size; + rootVisual.Children.InsertAtBottom(graphVisual); + + graphShape = compositor.CreateSpriteShape(); + // TODO: use accent theme resources + graphShape.FillBrush = compositor.CreateColorBrush(Colors.Transparent); + graphShape.StrokeBrush = compositor.CreateColorBrush(Colors.Turquoise); + graphVisual.Shapes.Add(graphShape); + + graphClip = compositor.CreateInsetClip(); + graphClip.RightInset = width; + rootVisual.Clip = graphClip; + + //// if it gets unloaded and reloaded because of the flyout closing + //if (Points.Count > 1) + UpdateGraph(); + + Points.CollectionChanged += PointsChanged; + } + + private void UserControl_Unloaded(object sender, RoutedEventArgs e) + { + rootVisual.Children.RemoveAll(); + graphVisual = null!; + graphShape = null!; + graphClip = null!; + Points.CollectionChanged -= PointsChanged; + } + + private void PointsChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + if (e.Action != NotifyCollectionChangedAction.Add) + return; + + if (Points[^1].Y > highestValue) + highestValue = Points[^1].Y; + + UpdateGraph(); + } + + void UpdateGraph() + { + var geometry = CreatePathFromPoints(); + graphShape.Geometry = geometry; + graphClip.RightInset = width - (width * Points[^1].X / 100f) + 1; + } + + CompositionPathGeometry CreatePathFromPoints() + { + using var pathBuilder = new CanvasPathBuilder(null); + pathBuilder.BeginFigure(0f, height); + for (int i = 0; i < Points.Count; i++) + { + // no smooth curve for now. a little ugly but maybe for the best performance-wise, we'll see before this gets merged + pathBuilder.AddLine(width * Points[i].X / 100f, height - (Points[i].Y / highestValue) * (height * 0.9f)); + } + pathBuilder.AddLine(width * Points[^1].X / 100f, height); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + var geometry = compositor.CreatePathGeometry(); + geometry.Path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); + return geometry; + } + } +} diff --git a/src/Files.App/UserControls/StatusCenter.xaml b/src/Files.App/UserControls/StatusCenter/StatusCenter.xaml similarity index 92% rename from src/Files.App/UserControls/StatusCenter.xaml rename to src/Files.App/UserControls/StatusCenter/StatusCenter.xaml index be5bb95e16a8..b9f194ff05db 100644 --- a/src/Files.App/UserControls/StatusCenter.xaml +++ b/src/Files.App/UserControls/StatusCenter/StatusCenter.xaml @@ -1,12 +1,12 @@  @@ -227,7 +227,7 @@ - - - - - - - + diff --git a/src/Files.App/UserControls/StatusCenter.xaml.cs b/src/Files.App/UserControls/StatusCenter/StatusCenter.xaml.cs similarity index 96% rename from src/Files.App/UserControls/StatusCenter.xaml.cs rename to src/Files.App/UserControls/StatusCenter/StatusCenter.xaml.cs index c54df0caaad1..c8904dad330f 100644 --- a/src/Files.App/UserControls/StatusCenter.xaml.cs +++ b/src/Files.App/UserControls/StatusCenter/StatusCenter.xaml.cs @@ -5,7 +5,7 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -namespace Files.App.UserControls +namespace Files.App.UserControls.StatusCenter { public sealed partial class StatusCenter : UserControl { diff --git a/src/Files.App/Utils/StatusCenter/StatusCenterItem.cs b/src/Files.App/Utils/StatusCenter/StatusCenterItem.cs index ee5c871942a3..b60b68177c5e 100644 --- a/src/Files.App/Utils/StatusCenter/StatusCenterItem.cs +++ b/src/Files.App/Utils/StatusCenter/StatusCenterItem.cs @@ -2,14 +2,8 @@ // Licensed under the MIT License. See the LICENSE. using System.Windows.Input; -using SkiaSharp; -using LiveChartsCore; -using LiveChartsCore.Drawing; -using LiveChartsCore.Kernel.Sketches; -using LiveChartsCore.SkiaSharpView; -using LiveChartsCore.SkiaSharpView.Painting; -using LiveChartsCore.Defaults; using Microsoft.UI.Xaml.Media; +using System.Numerics; namespace Files.App.Utils.StatusCenter { @@ -161,22 +155,6 @@ public StatusCenterItemProgressModel Progress public string? SubHeaderStringResource { get; private set; } - public ObservableCollection? SpeedGraphValues { get; private set; } - - public ObservableCollection? SpeedGraphBackgroundValues { get; private set; } - - public ObservableCollection? SpeedGraphSeries { get; private set; } - - public ObservableCollection? SpeedGraphBackgroundSeries { get; private set; } - - public ObservableCollection? SpeedGraphXAxes { get; private set; } - - public ObservableCollection? SpeedGraphYAxes { get; private set; } - - public ObservableCollection? SpeedGraphBackgroundXAxes { get; private set; } - - public ObservableCollection? SpeedGraphBackgroundYAxes { get; private set; } - public double IconBackgroundCircleBorderOpacity { get; private set; } public CancellationToken CancellationToken @@ -189,6 +167,8 @@ public string? HeaderTooltip private readonly CancellationTokenSource? _operationCancellationToken; + public readonly ObservableCollection SpeedGraphValues; + public ICommand CancelCommand { get; } public StatusCenterItem( @@ -218,7 +198,6 @@ public StatusCenterItem( IconBackgroundCircleBorderOpacity = 1; AnimatedIconState = "NormalOff"; SpeedGraphValues = []; - SpeedGraphBackgroundValues = []; CancelCommand = new RelayCommand(ExecuteCancelCommand); Message = "ProcessingItems".GetLocalizedResource(); Source = source; @@ -228,108 +207,6 @@ public StatusCenterItem( if (App.Current.Resources["App.Theme.FillColorAttentionBrush"] is not SolidColorBrush accentBrush) return; - // Initialize graph background fill series - SpeedGraphBackgroundSeries = - [ - new LineSeries - { - Values = SpeedGraphBackgroundValues, - GeometrySize = 0d, - DataPadding = new(0, 0), - IsHoverable = false, - - // Stroke - Stroke = new SolidColorPaint( - new(accentBrush.Color.R, accentBrush.Color.G, accentBrush.Color.B, 40), - 0.1f), - - // Fill under the stroke - Fill = new LinearGradientPaint( - [ - new(accentBrush.Color.R, accentBrush.Color.G, accentBrush.Color.B, 40) - ], - new(0f, 0f), - new(0f, 0f)), - } - ]; - - // Initialize graph series - SpeedGraphSeries = - [ - new LineSeries - { - Values = SpeedGraphValues, - GeometrySize = 0d, - DataPadding = new(0, 0), - IsHoverable = false, - - // Stroke - Stroke = new SolidColorPaint( - new(accentBrush.Color.R, accentBrush.Color.G, accentBrush.Color.B), - 1f), - - // Fill under the stroke - Fill = new LinearGradientPaint( - [ - new(accentBrush.Color.R, accentBrush.Color.G, accentBrush.Color.B, 50), - new(accentBrush.Color.R, accentBrush.Color.G, accentBrush.Color.B, 10) - ], - new(0f, 0f), - new(0f, 0f), - [0.1f, 1.0f]), - }, - ]; - - // Initialize X axes of the graph background fill - SpeedGraphBackgroundXAxes = - [ - new Axis - { - Padding = new Padding(0, 0), - Labels = new List(), - MaxLimit = 100, - ShowSeparatorLines = false, - } - ]; - - // Initialize X axes of the graph - SpeedGraphXAxes = - [ - new Axis - { - Padding = new Padding(0, 0), - Labels = new List(), - MaxLimit = 100, - ShowSeparatorLines = false, - } - ]; - - // Initialize Y axes of the graph background fill - SpeedGraphBackgroundYAxes = - [ - new Axis - { - Padding = new Padding(0, 0), - Labels = new List(), - ShowSeparatorLines = false, - MaxLimit = 100, - } - ]; - - // Initialize Y axes of the graph - SpeedGraphYAxes = - [ - new Axis - { - Padding = new Padding(0, 0), - Labels = new List(), - ShowSeparatorLines = false, - } - ]; - - SpeedGraphXAxes[0].SharedWith = SpeedGraphBackgroundXAxes; - SpeedGraphBackgroundXAxes[0].SharedWith = SpeedGraphXAxes; - // Set icon and initialize string resources switch (FileSystemOperationReturnResult) { @@ -450,7 +327,7 @@ private void ReportProgress(StatusCenterItemProgressModel value) OnPropertyChanged(nameof(HeaderTooltip)); // Graph item point - ObservablePoint point; + Vector2 point; // Set speed text and percentage switch (value.TotalSize, value.ItemsCount) @@ -461,7 +338,7 @@ private void ReportProgress(StatusCenterItemProgressModel value) SpeedText = $"{value.ProcessingSizeSpeed.ToSizeString()}/s"; - point = new(value.ProcessedSize * 100.0 / value.TotalSize, value.ProcessingSizeSpeed); + point = new((float)(value.ProcessedSize * 100.0 / value.TotalSize), (float)value.ProcessingSizeSpeed); break; // In progress, displaying processed size @@ -470,7 +347,7 @@ private void ReportProgress(StatusCenterItemProgressModel value) SpeedText = $"{value.ProcessingSizeSpeed.ToSizeString()}/s"; - point = new(value.ProcessedSize * 100.0 / value.TotalSize, value.ProcessingSizeSpeed); + point = new((float)(value.ProcessedSize * 100.0 / value.TotalSize), (float)value.ProcessingSizeSpeed); break; // In progress, displaying items count @@ -479,11 +356,11 @@ private void ReportProgress(StatusCenterItemProgressModel value) SpeedText = $"{value.ProcessingItemsCountSpeed:0} items/s"; - point = new(value.ProcessedItemsCount * 100.0 / value.ItemsCount, value.ProcessingItemsCountSpeed); + point = new((float)(value.ProcessedItemsCount * 100.0 / value.ItemsCount), (float)value.ProcessingItemsCountSpeed); break; default: - point = new(ProgressPercentage, value.ProcessingItemsCountSpeed); + point = new(ProgressPercentage, (float)value.ProcessingItemsCountSpeed); SpeedText = (value.ProcessedSize, value.ProcessedItemsCount) switch { @@ -497,22 +374,10 @@ private void ReportProgress(StatusCenterItemProgressModel value) bool isSamePoint = false; - // Remove the point that has the same X position - if (SpeedGraphValues?.FirstOrDefault(v => v.X == point.X) is ObservablePoint existingPoint) - { - SpeedGraphValues.Remove(existingPoint); - isSamePoint = true; - } - - // Add a new background fill point - if (!isSamePoint) - { - ObservablePoint newPoint = new(point.X, 100); - SpeedGraphBackgroundValues?.Add(newPoint); - } - // Add a new point - SpeedGraphValues?.Add(point); + // 'debounce' updates a bit so the graph isn't too noisy + if (SpeedGraphValues.Count == 0 || (point.X - SpeedGraphValues[^1].X) > 0.5) + SpeedGraphValues?.Add(point); // Add percentage to the header if (!IsIndeterminateProgress) From 3ef0f1616abf53a2291d5520e5a2f996cccb2fb7 Mon Sep 17 00:00:00 2001 From: heftymouse <59918974+heftymouse@users.noreply.github.com> Date: Thu, 6 Jun 2024 14:51:02 +0530 Subject: [PATCH 02/11] code that maybe works occassionally --- .../UserControls/StatusCenter/SpeedGraph.xaml | 5 +- .../StatusCenter/SpeedGraph.xaml.cs | 72 +++++++++++++------ 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml b/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml index cd9130943340..dcf39a9b50d0 100644 --- a/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml +++ b/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml @@ -9,7 +9,4 @@ xmlns:numerics="using:System.Numerics" Loaded="UserControl_Loaded" Unloaded="UserControl_Unloaded" - mc:Ignorable="d"> - - - + mc:Ignorable="d" /> diff --git a/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml.cs b/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml.cs index dd435af96fc4..10a85aea8199 100644 --- a/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml.cs +++ b/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml.cs @@ -4,6 +4,7 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Hosting; +using Microsoft.UI.Xaml.Media; using System.Collections.Specialized; using System.Numerics; using Windows.UI; @@ -24,7 +25,7 @@ public ObservableCollection Points SetValue(PointsProperty, value); } } - + public static readonly DependencyProperty PointsProperty = DependencyProperty.Register(nameof(Points), typeof(ObservableCollection), typeof(SpeedGraph), null); @@ -32,6 +33,7 @@ public ObservableCollection Points ContainerVisual rootVisual; ShapeVisual graphVisual; CompositionSpriteShape graphShape; + SpriteVisual line; InsetClip graphClip; float width; @@ -48,33 +50,52 @@ private void UserControl_Loaded(object sender, RoutedEventArgs e) { // is it a bad idea to recreate these on every load? // TODO: it doesn't work the first time you open the flyout - rootVisual = (ContainerVisual)ElementCompositionPreview.GetElementVisual(graphRoot); - compositor = rootVisual.Compositor; + var temp = ElementCompositionPreview.GetElementVisual(this); + compositor = temp.Compositor; + rootVisual = compositor.CreateContainerVisual(); + rootVisual.Size = temp.Size; + ElementCompositionPreview.SetElementChildVisual(this, rootVisual); width = rootVisual.Size.X; height = rootVisual.Size.Y; + var accentColor = (App.Current.Resources["AccentFillColorDefaultBrush"] as SolidColorBrush)!.Color; + var bgVisual = compositor.CreateSpriteVisual(); bgVisual.Size = rootVisual.Size; - bgVisual.Brush = compositor.CreateColorBrush(Color.FromArgb(0x20, 0x40, 0xE0, 0xD0)); - rootVisual.Children.InsertAtBottom(bgVisual); + bgVisual.Brush = compositor.CreateColorBrush(Color.FromArgb(0x15, accentColor.R, accentColor.G, accentColor.B)); graphVisual = compositor.CreateShapeVisual(); graphVisual.Size = rootVisual.Size; - rootVisual.Children.InsertAtBottom(graphVisual); graphShape = compositor.CreateSpriteShape(); - // TODO: use accent theme resources - graphShape.FillBrush = compositor.CreateColorBrush(Colors.Transparent); - graphShape.StrokeBrush = compositor.CreateColorBrush(Colors.Turquoise); + var gradientFill = compositor.CreateLinearGradientBrush(); + gradientFill.StartPoint = new(0.5f, 0f); + gradientFill.EndPoint = new(0.5f, 1f); + gradientFill.ColorStops.Add(compositor.CreateColorGradientStop(0f, Color.FromArgb(0x70, accentColor.R, accentColor.G, accentColor.B))); + gradientFill.ColorStops.Add(compositor.CreateColorGradientStop(1f, Color.FromArgb(0x06, accentColor.R, accentColor.G, accentColor.B))); + graphShape.FillBrush = gradientFill; + graphShape.StrokeBrush = compositor.CreateColorBrush(accentColor); + graphShape.StrokeThickness = 1f; graphVisual.Shapes.Add(graphShape); + var container = compositor.CreateContainerVisual(); + container.Size = rootVisual.Size; + container.Children.InsertAtBottom(bgVisual); + container.Children.InsertAtBottom(graphVisual); + rootVisual.Children.InsertAtBottom(container); + graphClip = compositor.CreateInsetClip(); graphClip.RightInset = width; - rootVisual.Clip = graphClip; + container.Clip = graphClip; - //// if it gets unloaded and reloaded because of the flyout closing - //if (Points.Count > 1) + line = compositor.CreateSpriteVisual(); + line.Size = new Vector2(width, 1.5f); + line.Brush = compositor.CreateColorBrush(accentColor); + rootVisual.Children.InsertAtTop(line); + + // if it gets unloaded and reloaded because of the flyout closing + if (Points.Count > 0) UpdateGraph(); Points.CollectionChanged += PointsChanged; @@ -91,12 +112,6 @@ private void UserControl_Unloaded(object sender, RoutedEventArgs e) private void PointsChanged(object? sender, NotifyCollectionChangedEventArgs e) { - if (e.Action != NotifyCollectionChangedAction.Add) - return; - - if (Points[^1].Y > highestValue) - highestValue = Points[^1].Y; - UpdateGraph(); } @@ -104,7 +119,18 @@ void UpdateGraph() { var geometry = CreatePathFromPoints(); graphShape.Geometry = geometry; - graphClip.RightInset = width - (width * Points[^1].X / 100f) + 1; + + var lineAnim = compositor.CreateScalarKeyFrameAnimation(); + lineAnim.InsertExpressionKeyFrame(0f, "this.StartingValue"); + lineAnim.InsertKeyFrame(1f, height - (Points[^1].Y / highestValue) * (height - 40f) - 4, compositor.CreateLinearEasingFunction()); + lineAnim.Duration = TimeSpan.FromMilliseconds(72); + line.StartAnimation("Offset.Y", lineAnim); + + var clipAnim = compositor.CreateScalarKeyFrameAnimation(); + clipAnim.InsertExpressionKeyFrame(0f, "this.StartingValue"); + clipAnim.InsertKeyFrame(1f, width - (width * Points[^1].X / 100f) - 2, compositor.CreateLinearEasingFunction()); + clipAnim.Duration = TimeSpan.FromMilliseconds(72); + graphClip.StartAnimation("RightInset", clipAnim); } CompositionPathGeometry CreatePathFromPoints() @@ -113,10 +139,14 @@ CompositionPathGeometry CreatePathFromPoints() pathBuilder.BeginFigure(0f, height); for (int i = 0; i < Points.Count; i++) { + if (Points[i].Y > highestValue) + highestValue = Points[i].Y; // no smooth curve for now. a little ugly but maybe for the best performance-wise, we'll see before this gets merged - pathBuilder.AddLine(width * Points[i].X / 100f, height - (Points[i].Y / highestValue) * (height * 0.9f)); + pathBuilder.AddLine(width * Points[i].X / 100f, height - (Points[i].Y / highestValue) * (height - 40f) - 4); } - pathBuilder.AddLine(width * Points[^1].X / 100f, height); + // little extra part so that steep lines don't get cut off + pathBuilder.AddLine(width * Points[^1].X / 100f + 3, height - (Points[^1].Y / highestValue) * (height - 40f) - 4); + pathBuilder.AddLine(width * Points[^1].X / 100f + 3, height); pathBuilder.EndFigure(CanvasFigureLoop.Closed); var geometry = compositor.CreatePathGeometry(); geometry.Path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); From bd3bd7e893124b50cb4a932fa88bca76610437ae Mon Sep 17 00:00:00 2001 From: heftymouse <59918974+heftymouse@users.noreply.github.com> Date: Fri, 7 Jun 2024 00:16:39 +0530 Subject: [PATCH 03/11] remove references and fix first time --- .../UserControls/StatusCenter/SpeedGraph.xaml | 4 +- .../StatusCenter/SpeedGraph.xaml.cs | 118 +++++++++++------- 2 files changed, 73 insertions(+), 49 deletions(-) diff --git a/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml b/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml index dcf39a9b50d0..a970c73ce987 100644 --- a/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml +++ b/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml @@ -6,7 +6,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="using:Files.App.UserControls.StatusCenter" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:numerics="using:System.Numerics" - Loaded="UserControl_Loaded" + ActualThemeChanged="UserControl_ActualThemeChanged" + SizeChanged="UserControl_SizeChanged" Unloaded="UserControl_Unloaded" mc:Ignorable="d" /> diff --git a/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml.cs b/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml.cs index 10a85aea8199..e97bbeabd8b0 100644 --- a/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml.cs +++ b/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml.cs @@ -19,23 +19,23 @@ public sealed partial class SpeedGraph : UserControl public ObservableCollection Points { get => (ObservableCollection)GetValue(PointsProperty); - set - { - highestValue = 0; - SetValue(PointsProperty, value); - } + set => SetValue(PointsProperty, value); } public static readonly DependencyProperty PointsProperty = DependencyProperty.Register(nameof(Points), typeof(ObservableCollection), typeof(SpeedGraph), null); Compositor compositor; + ContainerVisual rootVisual; - ShapeVisual graphVisual; + CompositionSpriteShape graphShape; - SpriteVisual line; InsetClip graphClip; + SpriteVisual line; + + bool initialized; + float width; float height; @@ -46,14 +46,42 @@ public SpeedGraph() this.InitializeComponent(); } - private void UserControl_Loaded(object sender, RoutedEventArgs e) + private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e) + { + if (initialized) + return; + + Init(); + + Points.CollectionChanged += PointsChanged; + // added after first load + this.Loaded += OnLoaded; + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + if (Points.Count > 0) + UpdateGraph(); + + Points.CollectionChanged += PointsChanged; + } + + private void UserControl_Unloaded(object sender, RoutedEventArgs e) + { + Points.CollectionChanged -= PointsChanged; + } + + private void PointsChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + UpdateGraph(); + } + + private void Init() { - // is it a bad idea to recreate these on every load? // TODO: it doesn't work the first time you open the flyout - var temp = ElementCompositionPreview.GetElementVisual(this); - compositor = temp.Compositor; + compositor = ElementCompositionPreview.GetElementVisual(this).Compositor; rootVisual = compositor.CreateContainerVisual(); - rootVisual.Size = temp.Size; + rootVisual.Size = this.ActualSize; ElementCompositionPreview.SetElementChildVisual(this, rootVisual); width = rootVisual.Size.X; @@ -61,21 +89,25 @@ private void UserControl_Loaded(object sender, RoutedEventArgs e) var accentColor = (App.Current.Resources["AccentFillColorDefaultBrush"] as SolidColorBrush)!.Color; + var backgroundBrush = compositor.CreateColorBrush(accentColor with { A = 0x15 }); + + var graphFillBrush = compositor.CreateLinearGradientBrush(); + graphFillBrush.StartPoint = new(0.5f, 0f); + graphFillBrush.EndPoint = new(0.5f, 1f); + graphFillBrush.ColorStops.Add(compositor.CreateColorGradientStop(0f, accentColor with { A = 0x70 })); + graphFillBrush.ColorStops.Add(compositor.CreateColorGradientStop(1f, accentColor with { A = 0x06 })); + + var graphStrokeBrush = compositor.CreateColorBrush(accentColor); + var bgVisual = compositor.CreateSpriteVisual(); bgVisual.Size = rootVisual.Size; - bgVisual.Brush = compositor.CreateColorBrush(Color.FromArgb(0x15, accentColor.R, accentColor.G, accentColor.B)); + bgVisual.Brush = backgroundBrush; - graphVisual = compositor.CreateShapeVisual(); + var graphVisual = compositor.CreateShapeVisual(); graphVisual.Size = rootVisual.Size; - graphShape = compositor.CreateSpriteShape(); - var gradientFill = compositor.CreateLinearGradientBrush(); - gradientFill.StartPoint = new(0.5f, 0f); - gradientFill.EndPoint = new(0.5f, 1f); - gradientFill.ColorStops.Add(compositor.CreateColorGradientStop(0f, Color.FromArgb(0x70, accentColor.R, accentColor.G, accentColor.B))); - gradientFill.ColorStops.Add(compositor.CreateColorGradientStop(1f, Color.FromArgb(0x06, accentColor.R, accentColor.G, accentColor.B))); - graphShape.FillBrush = gradientFill; - graphShape.StrokeBrush = compositor.CreateColorBrush(accentColor); + graphShape.FillBrush = graphFillBrush; + graphShape.StrokeBrush = graphStrokeBrush; graphShape.StrokeThickness = 1f; graphVisual.Shapes.Add(graphShape); @@ -83,36 +115,21 @@ private void UserControl_Loaded(object sender, RoutedEventArgs e) container.Size = rootVisual.Size; container.Children.InsertAtBottom(bgVisual); container.Children.InsertAtBottom(graphVisual); - rootVisual.Children.InsertAtBottom(container); graphClip = compositor.CreateInsetClip(); graphClip.RightInset = width; container.Clip = graphClip; + rootVisual.Children.InsertAtBottom(container); + line = compositor.CreateSpriteVisual(); - line.Size = new Vector2(width, 1.5f); - line.Brush = compositor.CreateColorBrush(accentColor); + line.Size = new(width, 1.5f); + line.Brush = graphStrokeBrush; rootVisual.Children.InsertAtTop(line); - // if it gets unloaded and reloaded because of the flyout closing - if (Points.Count > 0) - UpdateGraph(); - - Points.CollectionChanged += PointsChanged; - } - - private void UserControl_Unloaded(object sender, RoutedEventArgs e) - { - rootVisual.Children.RemoveAll(); - graphVisual = null!; - graphShape = null!; - graphClip = null!; - Points.CollectionChanged -= PointsChanged; - } + highestValue = 0; - private void PointsChanged(object? sender, NotifyCollectionChangedEventArgs e) - { - UpdateGraph(); + initialized = true; } void UpdateGraph() @@ -128,14 +145,14 @@ void UpdateGraph() var clipAnim = compositor.CreateScalarKeyFrameAnimation(); clipAnim.InsertExpressionKeyFrame(0f, "this.StartingValue"); - clipAnim.InsertKeyFrame(1f, width - (width * Points[^1].X / 100f) - 2, compositor.CreateLinearEasingFunction()); + clipAnim.InsertKeyFrame(1f, width - (width * Points[^1].X / 100f) - 1, compositor.CreateLinearEasingFunction()); clipAnim.Duration = TimeSpan.FromMilliseconds(72); graphClip.StartAnimation("RightInset", clipAnim); } CompositionPathGeometry CreatePathFromPoints() { - using var pathBuilder = new CanvasPathBuilder(null); + var pathBuilder = new CanvasPathBuilder(null); pathBuilder.BeginFigure(0f, height); for (int i = 0; i < Points.Count; i++) { @@ -145,12 +162,19 @@ CompositionPathGeometry CreatePathFromPoints() pathBuilder.AddLine(width * Points[i].X / 100f, height - (Points[i].Y / highestValue) * (height - 40f) - 4); } // little extra part so that steep lines don't get cut off - pathBuilder.AddLine(width * Points[^1].X / 100f + 3, height - (Points[^1].Y / highestValue) * (height - 40f) - 4); - pathBuilder.AddLine(width * Points[^1].X / 100f + 3, height); + pathBuilder.AddLine(width * Points[^1].X / 100f + 2, height - (Points[^1].Y / highestValue) * (height - 40f) - 4); + pathBuilder.AddLine(width * Points[^1].X / 100f + 2, height); pathBuilder.EndFigure(CanvasFigureLoop.Closed); var geometry = compositor.CreatePathGeometry(); geometry.Path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); return geometry; } + + private void UserControl_ActualThemeChanged(FrameworkElement sender, object args) + { + + } + + } } From 5aae362acddc309e7fdc71eca2974b1610e7968e Mon Sep 17 00:00:00 2001 From: heftymouse <59918974+heftymouse@users.noreply.github.com> Date: Fri, 7 Jun 2024 00:24:37 +0530 Subject: [PATCH 04/11] remove xaml --- .../{SpeedGraph.xaml.cs => SpeedGraph.cs} | 9 ++++++--- .../UserControls/StatusCenter/SpeedGraph.xaml | 12 ------------ 2 files changed, 6 insertions(+), 15 deletions(-) rename src/Files.App/UserControls/StatusCenter/{SpeedGraph.xaml.cs => SpeedGraph.cs} (95%) delete mode 100644 src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml diff --git a/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml.cs b/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs similarity index 95% rename from src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml.cs rename to src/Files.App/UserControls/StatusCenter/SpeedGraph.cs index e97bbeabd8b0..13bca7f5699b 100644 --- a/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml.cs +++ b/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs @@ -14,7 +14,7 @@ namespace Files.App.UserControls.StatusCenter { - public sealed partial class SpeedGraph : UserControl + public sealed partial class SpeedGraph : Control { public ObservableCollection Points { @@ -43,10 +43,13 @@ public ObservableCollection Points public SpeedGraph() { - this.InitializeComponent(); + // TODO: unhook + this.SizeChanged += OnSizeChanged; + this.Unloaded += UserControl_Unloaded; + this.ActualThemeChanged += UserControl_ActualThemeChanged; } - private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e) + private void OnSizeChanged(object sender, SizeChangedEventArgs e) { if (initialized) return; diff --git a/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml b/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml deleted file mode 100644 index a970c73ce987..000000000000 --- a/src/Files.App/UserControls/StatusCenter/SpeedGraph.xaml +++ /dev/null @@ -1,12 +0,0 @@ - - From 8643be80e766e8440ad757360bf84f75e1541048 Mon Sep 17 00:00:00 2001 From: heftymouse <59918974+heftymouse@users.noreply.github.com> Date: Fri, 7 Jun 2024 00:32:55 +0530 Subject: [PATCH 05/11] todone --- .../UserControls/StatusCenter/SpeedGraph.cs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs b/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs index 13bca7f5699b..8ab1e4acd095 100644 --- a/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs +++ b/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs @@ -56,8 +56,8 @@ private void OnSizeChanged(object sender, SizeChangedEventArgs e) Init(); + // added *after* first load Points.CollectionChanged += PointsChanged; - // added after first load this.Loaded += OnLoaded; } @@ -81,7 +81,6 @@ private void PointsChanged(object? sender, NotifyCollectionChangedEventArgs e) private void Init() { - // TODO: it doesn't work the first time you open the flyout compositor = ElementCompositionPreview.GetElementVisual(this).Compositor; rootVisual = compositor.CreateContainerVisual(); rootVisual.Size = this.ActualSize; @@ -135,18 +134,20 @@ private void Init() initialized = true; } + float YValue(float y) => height - (y / highestValue) * (height - 40f) - 4; + void UpdateGraph() { var geometry = CreatePathFromPoints(); graphShape.Geometry = geometry; - var lineAnim = compositor.CreateScalarKeyFrameAnimation(); + using var lineAnim = compositor.CreateScalarKeyFrameAnimation(); lineAnim.InsertExpressionKeyFrame(0f, "this.StartingValue"); - lineAnim.InsertKeyFrame(1f, height - (Points[^1].Y / highestValue) * (height - 40f) - 4, compositor.CreateLinearEasingFunction()); + lineAnim.InsertKeyFrame(1f, YValue(Points[^1].Y), compositor.CreateLinearEasingFunction()); lineAnim.Duration = TimeSpan.FromMilliseconds(72); line.StartAnimation("Offset.Y", lineAnim); - var clipAnim = compositor.CreateScalarKeyFrameAnimation(); + using var clipAnim = compositor.CreateScalarKeyFrameAnimation(); clipAnim.InsertExpressionKeyFrame(0f, "this.StartingValue"); clipAnim.InsertKeyFrame(1f, width - (width * Points[^1].X / 100f) - 1, compositor.CreateLinearEasingFunction()); clipAnim.Duration = TimeSpan.FromMilliseconds(72); @@ -155,29 +156,26 @@ void UpdateGraph() CompositionPathGeometry CreatePathFromPoints() { - var pathBuilder = new CanvasPathBuilder(null); + using var pathBuilder = new CanvasPathBuilder(null); pathBuilder.BeginFigure(0f, height); for (int i = 0; i < Points.Count; i++) { if (Points[i].Y > highestValue) highestValue = Points[i].Y; // no smooth curve for now. a little ugly but maybe for the best performance-wise, we'll see before this gets merged - pathBuilder.AddLine(width * Points[i].X / 100f, height - (Points[i].Y / highestValue) * (height - 40f) - 4); + pathBuilder.AddLine(width * Points[i].X / 100f, YValue(Points[i].Y)); } // little extra part so that steep lines don't get cut off - pathBuilder.AddLine(width * Points[^1].X / 100f + 2, height - (Points[^1].Y / highestValue) * (height - 40f) - 4); + pathBuilder.AddLine(width * Points[^1].X / 100f + 2, YValue(Points[^1].Y)); pathBuilder.AddLine(width * Points[^1].X / 100f + 2, height); pathBuilder.EndFigure(CanvasFigureLoop.Closed); var geometry = compositor.CreatePathGeometry(); geometry.Path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); return geometry; } - private void UserControl_ActualThemeChanged(FrameworkElement sender, object args) { } - - } } From 8546538ce562eab419e53205b65a80d90335d97e Mon Sep 17 00:00:00 2001 From: heftymouse <59918974+heftymouse@users.noreply.github.com> Date: Fri, 7 Jun 2024 18:01:53 +0530 Subject: [PATCH 06/11] oops regression --- src/Files.App/UserControls/StatusCenter/SpeedGraph.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs b/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs index 8ab1e4acd095..8a2191e1d814 100644 --- a/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs +++ b/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs @@ -19,7 +19,11 @@ public sealed partial class SpeedGraph : Control public ObservableCollection Points { get => (ObservableCollection)GetValue(PointsProperty); - set => SetValue(PointsProperty, value); + set + { + highestValue = 0; + SetValue(PointsProperty, value); + } } public static readonly DependencyProperty PointsProperty = From 5cff52712d085d7cada31cc9a27b0d3e00c61931 Mon Sep 17 00:00:00 2001 From: heftymouse <59918974+heftymouse@users.noreply.github.com> Date: Wed, 12 Jun 2024 18:27:41 +0530 Subject: [PATCH 07/11] changes --- .../UserControls/StatusCenter/SpeedGraph.cs | 42 +++++++++++-------- .../Utils/StatusCenter/StatusCenterItem.cs | 3 -- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs b/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs index 8a2191e1d814..d392b9b45320 100644 --- a/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs +++ b/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs @@ -33,11 +33,13 @@ public ObservableCollection Points ContainerVisual rootVisual; - CompositionSpriteShape graphShape; + CompositionPathGeometry graphGeometry; InsetClip graphClip; SpriteVisual line; + LinearEasingFunction linearEasing; + bool initialized; float width; @@ -90,8 +92,9 @@ private void Init() rootVisual.Size = this.ActualSize; ElementCompositionPreview.SetElementChildVisual(this, rootVisual); - width = rootVisual.Size.X; - height = rootVisual.Size.Y; + var size = rootVisual.Size; + width = size.X; + height = size.Y; var accentColor = (App.Current.Resources["AccentFillColorDefaultBrush"] as SolidColorBrush)!.Color; @@ -105,21 +108,23 @@ private void Init() var graphStrokeBrush = compositor.CreateColorBrush(accentColor); - var bgVisual = compositor.CreateSpriteVisual(); - bgVisual.Size = rootVisual.Size; - bgVisual.Brush = backgroundBrush; + var container = compositor.CreateSpriteVisual(); + container.Size = rootVisual.Size; + // container is also the graph background + container.Brush = backgroundBrush; var graphVisual = compositor.CreateShapeVisual(); graphVisual.Size = rootVisual.Size; - graphShape = compositor.CreateSpriteShape(); + var graphShape = compositor.CreateSpriteShape(); graphShape.FillBrush = graphFillBrush; graphShape.StrokeBrush = graphStrokeBrush; graphShape.StrokeThickness = 1f; + + graphGeometry = compositor.CreatePathGeometry(); + graphShape.Geometry = graphGeometry; + graphVisual.Shapes.Add(graphShape); - var container = compositor.CreateContainerVisual(); - container.Size = rootVisual.Size; - container.Children.InsertAtBottom(bgVisual); container.Children.InsertAtBottom(graphVisual); graphClip = compositor.CreateInsetClip(); @@ -135,6 +140,8 @@ private void Init() highestValue = 0; + linearEasing = compositor.CreateLinearEasingFunction(); + initialized = true; } @@ -142,23 +149,23 @@ private void Init() void UpdateGraph() { - var geometry = CreatePathFromPoints(); - graphShape.Geometry = geometry; + var path = CreatePathFromPoints(); + graphGeometry.Path = path; using var lineAnim = compositor.CreateScalarKeyFrameAnimation(); lineAnim.InsertExpressionKeyFrame(0f, "this.StartingValue"); - lineAnim.InsertKeyFrame(1f, YValue(Points[^1].Y), compositor.CreateLinearEasingFunction()); + lineAnim.InsertKeyFrame(1f, YValue(Points[^1].Y), linearEasing); lineAnim.Duration = TimeSpan.FromMilliseconds(72); line.StartAnimation("Offset.Y", lineAnim); using var clipAnim = compositor.CreateScalarKeyFrameAnimation(); clipAnim.InsertExpressionKeyFrame(0f, "this.StartingValue"); - clipAnim.InsertKeyFrame(1f, width - (width * Points[^1].X / 100f) - 1, compositor.CreateLinearEasingFunction()); + clipAnim.InsertKeyFrame(1f, width - (width * Points[^1].X / 100f) - 1, linearEasing); clipAnim.Duration = TimeSpan.FromMilliseconds(72); graphClip.StartAnimation("RightInset", clipAnim); } - CompositionPathGeometry CreatePathFromPoints() + CompositionPath CreatePathFromPoints() { using var pathBuilder = new CanvasPathBuilder(null); pathBuilder.BeginFigure(0f, height); @@ -173,9 +180,8 @@ CompositionPathGeometry CreatePathFromPoints() pathBuilder.AddLine(width * Points[^1].X / 100f + 2, YValue(Points[^1].Y)); pathBuilder.AddLine(width * Points[^1].X / 100f + 2, height); pathBuilder.EndFigure(CanvasFigureLoop.Closed); - var geometry = compositor.CreatePathGeometry(); - geometry.Path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); - return geometry; + var path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); + return path; } private void UserControl_ActualThemeChanged(FrameworkElement sender, object args) { diff --git a/src/Files.App/Utils/StatusCenter/StatusCenterItem.cs b/src/Files.App/Utils/StatusCenter/StatusCenterItem.cs index b60b68177c5e..7a259f10793d 100644 --- a/src/Files.App/Utils/StatusCenter/StatusCenterItem.cs +++ b/src/Files.App/Utils/StatusCenter/StatusCenterItem.cs @@ -372,9 +372,6 @@ private void ReportProgress(StatusCenterItemProgressModel value) break; } - bool isSamePoint = false; - - // Add a new point // 'debounce' updates a bit so the graph isn't too noisy if (SpeedGraphValues.Count == 0 || (point.X - SpeedGraphValues[^1].X) > 0.5) SpeedGraphValues?.Add(point); From a0b374718863a8ffae31dd5e2404432e5b9b303d Mon Sep 17 00:00:00 2001 From: heftymouse <59918974+heftymouse@users.noreply.github.com> Date: Sat, 15 Jun 2024 21:01:02 +0530 Subject: [PATCH 08/11] IT IS DONE. --- .../UserControls/StatusCenter/SpeedGraph.cs | 93 +++++++++++++++---- 1 file changed, 73 insertions(+), 20 deletions(-) diff --git a/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs b/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs index d392b9b45320..1b0104ced7e2 100644 --- a/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs +++ b/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs @@ -1,5 +1,4 @@ using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.UI; using Microsoft.UI.Composition; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -7,7 +6,6 @@ using Microsoft.UI.Xaml.Media; using System.Collections.Specialized; using System.Numerics; -using Windows.UI; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -38,6 +36,11 @@ public ObservableCollection Points SpriteVisual line; + CompositionColorBrush backgroundBrush; + CompositionColorGradientStop graphFillBottom; + CompositionColorGradientStop graphFillTop; + CompositionColorBrush graphStrokeBrush; + LinearEasingFunction linearEasing; bool initialized; @@ -46,13 +49,16 @@ public ObservableCollection Points float height; float highestValue; + + IAppThemeModeService themeModeService; public SpeedGraph() - { - // TODO: unhook + { + compositor = ElementCompositionPreview.GetElementVisual(this).Compositor; + + themeModeService = Ioc.Default.GetRequiredService(); + this.SizeChanged += OnSizeChanged; - this.Unloaded += UserControl_Unloaded; - this.ActualThemeChanged += UserControl_ActualThemeChanged; } private void OnSizeChanged(object sender, SizeChangedEventArgs e) @@ -60,34 +66,59 @@ private void OnSizeChanged(object sender, SizeChangedEventArgs e) if (initialized) return; - Init(); + InitGraph(); + + this.SizeChanged -= OnSizeChanged; // added *after* first load - Points.CollectionChanged += PointsChanged; this.Loaded += OnLoaded; + this.Unloaded += OnUnloaded; + Points.CollectionChanged += OnPointsChanged; + this.ActualThemeChanged += OnThemeChanged; + themeModeService.AppThemeModeChanged += OnAppThemeModeChanged; } private void OnLoaded(object sender, RoutedEventArgs e) { if (Points.Count > 0) + { + SetGraphColors(); UpdateGraph(); + } - Points.CollectionChanged += PointsChanged; + this.Unloaded += OnUnloaded; + Points.CollectionChanged += OnPointsChanged; + this.ActualThemeChanged += OnThemeChanged; + themeModeService.AppThemeModeChanged += OnAppThemeModeChanged; } - private void UserControl_Unloaded(object sender, RoutedEventArgs e) + private void OnUnloaded(object sender, RoutedEventArgs e) { - Points.CollectionChanged -= PointsChanged; + this.Unloaded -= OnUnloaded; + Points.CollectionChanged -= OnPointsChanged; + this.ActualThemeChanged -= OnThemeChanged; + themeModeService.AppThemeModeChanged -= OnAppThemeModeChanged; } - private void PointsChanged(object? sender, NotifyCollectionChangedEventArgs e) + private void OnPointsChanged(object? sender, NotifyCollectionChangedEventArgs e) { UpdateGraph(); } - private void Init() + private void OnAppThemeModeChanged(object? sender, EventArgs e) + { + if (initialized) + SetGraphColors(); + } + + private void OnThemeChanged(FrameworkElement sender, object args) + { + if (initialized) + SetGraphColors(); + } + + private void InitGraph() { - compositor = ElementCompositionPreview.GetElementVisual(this).Compositor; rootVisual = compositor.CreateContainerVisual(); rootVisual.Size = this.ActualSize; ElementCompositionPreview.SetElementChildVisual(this, rootVisual); @@ -96,17 +127,30 @@ private void Init() width = size.X; height = size.Y; + var rootClip = compositor.CreateRectangleClip(); + rootClip.Top = 1.5f; + rootClip.Left = 1.5f; + rootClip.Bottom = height - 1.5f; + rootClip.Right = width - 2f; // i fear i might be a victim of DPI scaling here + rootClip.TopLeftRadius = new(4f); + rootClip.TopRightRadius = new(4f); + rootClip.BottomLeftRadius = new(4f); + rootClip.BottomRightRadius = new(4f); + rootVisual.Clip = rootClip; + var accentColor = (App.Current.Resources["AccentFillColorDefaultBrush"] as SolidColorBrush)!.Color; - var backgroundBrush = compositor.CreateColorBrush(accentColor with { A = 0x15 }); + backgroundBrush = compositor.CreateColorBrush(accentColor with { A = 0x0f }); var graphFillBrush = compositor.CreateLinearGradientBrush(); graphFillBrush.StartPoint = new(0.5f, 0f); graphFillBrush.EndPoint = new(0.5f, 1f); - graphFillBrush.ColorStops.Add(compositor.CreateColorGradientStop(0f, accentColor with { A = 0x70 })); - graphFillBrush.ColorStops.Add(compositor.CreateColorGradientStop(1f, accentColor with { A = 0x06 })); + graphFillTop = compositor.CreateColorGradientStop(0f, accentColor with { A = 0x7f }); + graphFillBottom = compositor.CreateColorGradientStop(1f, accentColor with { A = 0x0f }); + graphFillBrush.ColorStops.Add(graphFillBottom); + graphFillBrush.ColorStops.Add(graphFillTop); - var graphStrokeBrush = compositor.CreateColorBrush(accentColor); + graphStrokeBrush = compositor.CreateColorBrush(accentColor); var container = compositor.CreateSpriteVisual(); container.Size = rootVisual.Size; @@ -136,6 +180,7 @@ private void Init() line = compositor.CreateSpriteVisual(); line.Size = new(width, 1.5f); line.Brush = graphStrokeBrush; + line.Offset = new(0f, height - 4f, 0); rootVisual.Children.InsertAtTop(line); highestValue = 0; @@ -145,7 +190,7 @@ private void Init() initialized = true; } - float YValue(float y) => height - (y / highestValue) * (height - 40f) - 4; + float YValue(float y) => height - (y / highestValue) * (height - 48f) - 4; void UpdateGraph() { @@ -183,9 +228,17 @@ CompositionPath CreatePathFromPoints() var path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); return path; } - private void UserControl_ActualThemeChanged(FrameworkElement sender, object args) + + void SetGraphColors() { + var accentColor = (App.Current.Resources["AccentFillColorDefaultBrush"] as SolidColorBrush)!.Color; + + backgroundBrush.Color = accentColor with { A = 0x0f }; + + graphFillTop.Color = accentColor with { A = 0x7f }; + graphFillBottom.Color = accentColor with { A = 0x0f }; + graphStrokeBrush.Color = accentColor; } } } From cba81e5df3d4457eab952d2b5048385b9b5fa598 Mon Sep 17 00:00:00 2001 From: heftymouse <59918974+heftymouse@users.noreply.github.com> Date: Sun, 16 Jun 2024 15:38:38 +0530 Subject: [PATCH 09/11] fix accent color --- .../Data/Contracts/IAppThemeModeService.cs | 6 +++++ .../Services/App/AppThemeModeService.cs | 16 ++++++++++- .../UserControls/StatusCenter/SpeedGraph.cs | 27 ++++++++++--------- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/Files.App/Data/Contracts/IAppThemeModeService.cs b/src/Files.App/Data/Contracts/IAppThemeModeService.cs index 28311d8950b4..bb9dbfefbb13 100644 --- a/src/Files.App/Data/Contracts/IAppThemeModeService.cs +++ b/src/Files.App/Data/Contracts/IAppThemeModeService.cs @@ -3,6 +3,7 @@ using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; +using Windows.UI; namespace Files.App.Data.Contracts { @@ -18,6 +19,11 @@ public interface IAppThemeModeService /// public ElementTheme AppThemeMode { get; set; } + /// + /// Gets the default accent fill color for the current theme mode. + /// + public Color DefaultAccentColor { get; } + /// /// Refreshes the application theme mode only for the main window. /// diff --git a/src/Files.App/Services/App/AppThemeModeService.cs b/src/Files.App/Services/App/AppThemeModeService.cs index b9e9c4c51358..c15085881d8d 100644 --- a/src/Files.App/Services/App/AppThemeModeService.cs +++ b/src/Files.App/Services/App/AppThemeModeService.cs @@ -5,6 +5,7 @@ using Microsoft.UI; using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Media; using Windows.Storage; using Windows.UI; using Windows.UI.ViewManagement; @@ -34,6 +35,19 @@ public ElementTheme AppThemeMode } } + /// + public Color DefaultAccentColor + { + get => AppThemeMode switch + { + // these values are from the definition of AccentFillColorDefaultBrush in generic.xaml + // will have to be updated if WinUI changes in the future + ElementTheme.Light => (Color)(App.Current.Resources["SystemAccentColorDark1"]), + ElementTheme.Dark => (Color)(App.Current.Resources["SystemAccentColorLight2"]), + ElementTheme.Default or _ => (App.Current.Resources["AccentFillColorDefaultBrush"] as SolidColorBrush)!.Color + }; + } + /// public event EventHandler? AppThemeModeChanged; @@ -99,7 +113,7 @@ public void SetAppThemeMode(Window? window = null, AppWindowTitleBar? titleBar = } catch (Exception ex) { - App.Logger.LogWarning(ex, "Failed to change theme mode of the app."); + App.Logger.LogWarning(ex, "Failed to change theme mode of the app."); } } diff --git a/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs b/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs index 1b0104ced7e2..e55dc9be9395 100644 --- a/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs +++ b/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs @@ -3,7 +3,6 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Hosting; -using Microsoft.UI.Xaml.Media; using System.Collections.Specialized; using System.Numerics; @@ -107,6 +106,7 @@ private void OnPointsChanged(object? sender, NotifyCollectionChangedEventArgs e) private void OnAppThemeModeChanged(object? sender, EventArgs e) { + // this seemingly doesn't fire? leaving it here in case it does in the future (if it's ever used outside of the flyout) if (initialized) SetGraphColors(); } @@ -131,26 +131,28 @@ private void InitGraph() rootClip.Top = 1.5f; rootClip.Left = 1.5f; rootClip.Bottom = height - 1.5f; - rootClip.Right = width - 2f; // i fear i might be a victim of DPI scaling here + rootClip.Right = width - 2f; rootClip.TopLeftRadius = new(4f); rootClip.TopRightRadius = new(4f); rootClip.BottomLeftRadius = new(4f); rootClip.BottomRightRadius = new(4f); rootVisual.Clip = rootClip; - var accentColor = (App.Current.Resources["AccentFillColorDefaultBrush"] as SolidColorBrush)!.Color; - - backgroundBrush = compositor.CreateColorBrush(accentColor with { A = 0x0f }); + backgroundBrush = compositor.CreateColorBrush(); var graphFillBrush = compositor.CreateLinearGradientBrush(); graphFillBrush.StartPoint = new(0.5f, 0f); graphFillBrush.EndPoint = new(0.5f, 1f); - graphFillTop = compositor.CreateColorGradientStop(0f, accentColor with { A = 0x7f }); - graphFillBottom = compositor.CreateColorGradientStop(1f, accentColor with { A = 0x0f }); + graphFillTop = compositor.CreateColorGradientStop(); + graphFillTop.Offset = 0f; + graphFillBottom = compositor.CreateColorGradientStop(); + graphFillBottom.Offset = 1f; graphFillBrush.ColorStops.Add(graphFillBottom); graphFillBrush.ColorStops.Add(graphFillTop); - graphStrokeBrush = compositor.CreateColorBrush(accentColor); + graphStrokeBrush = compositor.CreateColorBrush(); + + SetGraphColors(); var container = compositor.CreateSpriteVisual(); container.Size = rootVisual.Size; @@ -198,13 +200,11 @@ void UpdateGraph() graphGeometry.Path = path; using var lineAnim = compositor.CreateScalarKeyFrameAnimation(); - lineAnim.InsertExpressionKeyFrame(0f, "this.StartingValue"); lineAnim.InsertKeyFrame(1f, YValue(Points[^1].Y), linearEasing); lineAnim.Duration = TimeSpan.FromMilliseconds(72); line.StartAnimation("Offset.Y", lineAnim); using var clipAnim = compositor.CreateScalarKeyFrameAnimation(); - clipAnim.InsertExpressionKeyFrame(0f, "this.StartingValue"); clipAnim.InsertKeyFrame(1f, width - (width * Points[^1].X / 100f) - 1, linearEasing); clipAnim.Duration = TimeSpan.FromMilliseconds(72); graphClip.StartAnimation("RightInset", clipAnim); @@ -231,12 +231,13 @@ CompositionPath CreatePathFromPoints() void SetGraphColors() { - var accentColor = (App.Current.Resources["AccentFillColorDefaultBrush"] as SolidColorBrush)!.Color; + var accentColor = themeModeService.DefaultAccentColor; - backgroundBrush.Color = accentColor with { A = 0x0f }; + var veryLightColor = accentColor with { A = 0x0f }; + backgroundBrush.Color = veryLightColor; graphFillTop.Color = accentColor with { A = 0x7f }; - graphFillBottom.Color = accentColor with { A = 0x0f }; + graphFillBottom.Color = veryLightColor; graphStrokeBrush.Color = accentColor; } From 51499a7d82ed39cfd6b7e4a699b46f67fc605fa2 Mon Sep 17 00:00:00 2001 From: heftymouse <59918974+heftymouse@users.noreply.github.com> Date: Sun, 16 Jun 2024 16:07:43 +0530 Subject: [PATCH 10/11] actually fix the colors this time --- src/Files.App/UserControls/StatusCenter/SpeedGraph.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs b/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs index e55dc9be9395..5aa80607dd6d 100644 --- a/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs +++ b/src/Files.App/UserControls/StatusCenter/SpeedGraph.cs @@ -3,8 +3,10 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Hosting; +using Microsoft.UI.Xaml.Media; using System.Collections.Specialized; using System.Numerics; +using Windows.UI; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -234,9 +236,16 @@ void SetGraphColors() var accentColor = themeModeService.DefaultAccentColor; var veryLightColor = accentColor with { A = 0x0f }; + + var slightlyDarkerColor = this.ActualTheme switch + { + ElementTheme.Light => accentColor with { A = 0x55 }, + _ => accentColor with { A = 0x7f } + }; + backgroundBrush.Color = veryLightColor; - graphFillTop.Color = accentColor with { A = 0x7f }; + graphFillTop.Color = slightlyDarkerColor; graphFillBottom.Color = veryLightColor; graphStrokeBrush.Color = accentColor; From 6ce21bceb5c0bbec9a0cf7c6215f18d2d48987a7 Mon Sep 17 00:00:00 2001 From: Yair <39923744+yaira2@users.noreply.github.com> Date: Mon, 17 Jun 2024 17:45:49 -0400 Subject: [PATCH 11/11] Update StatusCenter.xaml --- src/Files.App/UserControls/StatusCenter/StatusCenter.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Files.App/UserControls/StatusCenter/StatusCenter.xaml b/src/Files.App/UserControls/StatusCenter/StatusCenter.xaml index b9f194ff05db..22f968754810 100644 --- a/src/Files.App/UserControls/StatusCenter/StatusCenter.xaml +++ b/src/Files.App/UserControls/StatusCenter/StatusCenter.xaml @@ -227,7 +227,7 @@