From 39ede679c9e109144a35ac04875243e3906970de Mon Sep 17 00:00:00 2001 From: "Marco.Silipo" Date: Tue, 17 Mar 2020 16:31:38 +0100 Subject: [PATCH] Added Background Tasks view --- Arma.Studio.Data/ArmA.Studio.Data.csproj | 11 + Arma.Studio.Data/IO/File.cs | 12 - Arma.Studio.Data/Themes/Generic.xaml | 2 + .../UI/Converters/OnEqualsConverter.cs | 30 ++ Arma.Studio.Data/UI/PopupContainer.cs | 67 ++++ Arma.Studio.Data/UI/PopupContainer.xaml | 42 +++ Arma.Studio.Data/UI/ProgressSpinner.cs | 300 ++++++++++++++++++ Arma.Studio.Data/UI/ProgressSpinner.xaml | 13 + Arma.Studio/App.xaml | 23 ++ Arma.Studio/UI/Windows/MainWindow.xaml | 133 ++++++++ 10 files changed, 621 insertions(+), 12 deletions(-) create mode 100644 Arma.Studio.Data/UI/Converters/OnEqualsConverter.cs create mode 100644 Arma.Studio.Data/UI/PopupContainer.cs create mode 100644 Arma.Studio.Data/UI/PopupContainer.xaml create mode 100644 Arma.Studio.Data/UI/ProgressSpinner.cs create mode 100644 Arma.Studio.Data/UI/ProgressSpinner.xaml diff --git a/Arma.Studio.Data/ArmA.Studio.Data.csproj b/Arma.Studio.Data/ArmA.Studio.Data.csproj index bc05829..7c2962c 100644 --- a/Arma.Studio.Data/ArmA.Studio.Data.csproj +++ b/Arma.Studio.Data/ArmA.Studio.Data.csproj @@ -147,6 +147,7 @@ + @@ -171,6 +172,8 @@ + + @@ -204,6 +207,14 @@ MSBuild:Compile Designer + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile Designer diff --git a/Arma.Studio.Data/IO/File.cs b/Arma.Studio.Data/IO/File.cs index c1e90b8..a87a014 100644 --- a/Arma.Studio.Data/IO/File.cs +++ b/Arma.Studio.Data/IO/File.cs @@ -8,18 +8,6 @@ namespace Arma.Studio.Data.IO { public class File : FileFolderBase { - public PBO PBO - { - get - { - FileFolderBase ffb = this; - while (ffb != null && !(ffb is PBO)) - { - ffb = ffb.Parent; - } - return ffb as PBO; - } - } public string Extension => System.IO.Path.GetExtension(this.Name); protected override void OnNameChanged() { diff --git a/Arma.Studio.Data/Themes/Generic.xaml b/Arma.Studio.Data/Themes/Generic.xaml index c9c4e52..06e797f 100644 --- a/Arma.Studio.Data/Themes/Generic.xaml +++ b/Arma.Studio.Data/Themes/Generic.xaml @@ -2,6 +2,8 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + + \ No newline at end of file diff --git a/Arma.Studio.Data/UI/Converters/OnEqualsConverter.cs b/Arma.Studio.Data/UI/Converters/OnEqualsConverter.cs new file mode 100644 index 0000000..b6b0477 --- /dev/null +++ b/Arma.Studio.Data/UI/Converters/OnEqualsConverter.cs @@ -0,0 +1,30 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace Arma.Studio.Data.UI.Converters +{ + public class OnEqualsConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (!(parameter is string pstring)) + { + return value.GetType().IsEquivalentTo(parameter.GetType()) ? value.Equals(parameter) : value; + } + string[] parr = pstring.Split('|'); + var valueType = value.GetType(); + if (value.Equals(valueType.IsEnum ? Enum.Parse(valueType, parr[0]) : System.Convert.ChangeType(parr[0], value.GetType()))) + { + return System.Convert.ChangeType(parr[1], targetType); + } + + return parr.Length == 3 ? System.Convert.ChangeType(parr[2], targetType) : value; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return value; + } + } +} diff --git a/Arma.Studio.Data/UI/PopupContainer.cs b/Arma.Studio.Data/UI/PopupContainer.cs new file mode 100644 index 0000000..dfa0573 --- /dev/null +++ b/Arma.Studio.Data/UI/PopupContainer.cs @@ -0,0 +1,67 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Markup; + +namespace Arma.Studio.Data.UI +{ + [ContentProperty(nameof(PopupContainer.Content))] + public class PopupContainer : Control + { + static PopupContainer() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(PopupContainer), new FrameworkPropertyMetadata(typeof(PopupContainer))); + } + + #region DependencyProperty: Content (System.Object) + public static readonly DependencyProperty ContentProperty = DependencyProperty.Register( + nameof(Content), typeof(object), typeof(PopupContainer)); + public object Content { get => this.GetValue(ContentProperty); set => this.SetValue(ContentProperty, value); } + #endregion + #region DependencyProperty: Placement (System.Windows.Controls.Primitives.PlacementMode) + public static readonly DependencyProperty PlacementProperty = DependencyProperty.Register( + nameof(Placement), typeof(PlacementMode), typeof(PopupContainer)); + public PlacementMode Placement { get => (PlacementMode)this.GetValue(PlacementProperty); set => this.SetValue(PlacementProperty, value); } + #endregion + #region DependencyProperty: PopupContent (System.Object) + public static readonly DependencyProperty PopupContentProperty = DependencyProperty.Register( + nameof(PopupContent), typeof(object), typeof(PopupContainer)); + public object PopupContent { get => this.GetValue(PopupContentProperty); set => this.SetValue(PopupContentProperty, value); } + #endregion + #region DependencyProperty: PopupContentTemplate (System.Windows.DataTemplate) + public static readonly DependencyProperty PopupContentTemplateProperty = DependencyProperty.Register( + nameof(PopupContentTemplate), typeof(DataTemplate), typeof(PopupContainer)); + public DataTemplate PopupContentTemplate { get => (DataTemplate)this.GetValue(PopupContentTemplateProperty); set => this.SetValue(PopupContentTemplateProperty, value); } + #endregion + #region DependencyProperty: PopupCanOpen (System.Boolean) + public static readonly DependencyProperty PopupCanOpenProperty = DependencyProperty.Register( + nameof(PopupCanOpen), typeof(bool), typeof(PopupContainer), new PropertyMetadata(true)); + public bool PopupCanOpen { get => (bool)this.GetValue(PopupCanOpenProperty); set => this.SetValue(PopupCanOpenProperty, value); } + #endregion + #region RoutedEvent: PopupOpened + public static readonly RoutedEvent PopupOpenedEvent = EventManager.RegisterRoutedEvent(nameof(PopupOpened), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(PopupContainer)); + public event RoutedEventHandler PopupOpened { add { this.AddHandler(PopupOpenedEvent, value); } remove { this.RemoveHandler(PopupOpenedEvent, value); } } + protected void RaisePopupOpened() { this.RaiseEvent(new RoutedEventArgs(PopupOpenedEvent, this)); } + #endregion + + public override void OnApplyTemplate() + { + if (this.GetTemplateChild("PART_Popup") is Popup PART_Popup) + { + PART_Popup.Opened += this.PART_Popup_Opened; + PART_Popup.PreviewMouseDown += this.PART_Popup_PreviewMouseDown; + } + } + + private void PART_Popup_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) + { + Application.Current.MainWindow.Activate(); + } + + private void PART_Popup_Opened(object sender, EventArgs e) + { + this.RaisePopupOpened(); + } + } +} diff --git a/Arma.Studio.Data/UI/PopupContainer.xaml b/Arma.Studio.Data/UI/PopupContainer.xaml new file mode 100644 index 0000000..9e5823a --- /dev/null +++ b/Arma.Studio.Data/UI/PopupContainer.xaml @@ -0,0 +1,42 @@ + + + \ No newline at end of file diff --git a/Arma.Studio.Data/UI/ProgressSpinner.cs b/Arma.Studio.Data/UI/ProgressSpinner.cs new file mode 100644 index 0000000..c5666ed --- /dev/null +++ b/Arma.Studio.Data/UI/ProgressSpinner.cs @@ -0,0 +1,300 @@ +using System; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Animation; + +namespace Arma.Studio.Data.UI +{ + public class ProgressSpinner : FrameworkElement + { + static ProgressSpinner() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(ProgressSpinner), new FrameworkPropertyMetadata(typeof(ProgressSpinner))); + } + #region DependencyProperty: FragmentActive [AffectsRender] (System.Windows.Media.Color) + public static readonly DependencyProperty FragmentActiveProperty = DependencyProperty.Register( + "FragmentActive", + typeof(Color), + typeof(ProgressSpinner), + new FrameworkPropertyMetadata(Colors.Black, FrameworkPropertyMetadataOptions.AffectsRender) + ); + public Color FragmentActive + { + get => (Color)this.GetValue(FragmentActiveProperty); + set => this.SetValue(FragmentActiveProperty, value); + } + + #endregion + #region DependencyProperty: FragmentInactive [AffectsRender] (System.Windows.Media.Color) + public static readonly DependencyProperty FragmentInactiveProperty = DependencyProperty.Register( + "FragmentInactive", + typeof(Color), + typeof(ProgressSpinner), + new FrameworkPropertyMetadata(Colors.LightGray, FrameworkPropertyMetadataOptions.AffectsRender) + ); + public Color FragmentInactive + { + get => (Color)this.GetValue(FragmentInactiveProperty); + set => this.SetValue(FragmentInactiveProperty, value); + } + + #endregion + #region DependencyProperty: FragmentBorderFill [AffectsRender] (System.Windows.Media.Brush) + public static readonly DependencyProperty FragmentBorderFillProperty = DependencyProperty.Register( + nameof(FragmentBorderFill), + typeof(Brush), + typeof(ProgressSpinner), + new FrameworkPropertyMetadata(Brushes.White, FrameworkPropertyMetadataOptions.AffectsRender) + ); + public Brush FragmentBorderFill + { + get => this.GetValue(FragmentBorderFillProperty) as Brush; + set => this.SetValue(FragmentBorderFillProperty, value); + } + + #endregion + #region DependencyProperty: FragmentBorderThickness [AffectsRender] (System.Int32) + public static readonly DependencyProperty FragmentBorderThicknessProperty = DependencyProperty.Register( + "FragmentBorderThickness", + typeof(int), + typeof(ProgressSpinner), + new FrameworkPropertyMetadata(1, FrameworkPropertyMetadataOptions.AffectsRender) + ); + public int FragmentBorderThickness + { + get => (int)this.GetValue(FragmentBorderThicknessProperty); + set => this.SetValue(FragmentBorderThicknessProperty, value); + } + + #endregion + #region DependencyProperty: InnerDistance [AffectsRender] (System.Double) + public static readonly DependencyProperty InnerDistanceProperty = DependencyProperty.Register( + "InnerDistance", + typeof(double), + typeof(ProgressSpinner), + new FrameworkPropertyMetadata(0.5, FrameworkPropertyMetadataOptions.AffectsRender) + ); + public double InnerDistance + { + get => (double)this.GetValue(InnerDistanceProperty); + set => this.SetValue(InnerDistanceProperty, value); + } + + #endregion + #region DependencyProperty: OuterDistance [AffectsRender] (System.Double) + public static readonly DependencyProperty OuterDistanceProperty = DependencyProperty.Register( + "OuterDistance", + typeof(double), + typeof(ProgressSpinner), + new FrameworkPropertyMetadata(0.25, FrameworkPropertyMetadataOptions.AffectsRender) + ); + public double OuterDistance + { + get => (double)this.GetValue(OuterDistanceProperty); + set => this.SetValue(OuterDistanceProperty, value); + } + + #endregion + #region DependencyProperty: FragmentCount [PropertyChangedCallback, AffectsRender] (System.Int32) + public static readonly DependencyProperty FragmentCountProperty = DependencyProperty.Register( + "ElementCount", + typeof(int), + typeof(ProgressSpinner), + new FrameworkPropertyMetadata(8, FrameworkPropertyMetadataOptions.AffectsRender, FragmentCount_PropertyChangedCallback) + ); + public int FragmentCount + { + get => (int)this.GetValue(FragmentCountProperty); + set => this.SetValue(FragmentCountProperty, value); + } + private static void FragmentCount_PropertyChangedCallback(DependencyObject target, DependencyPropertyChangedEventArgs e) + { + if (!(target is ProgressSpinner spinner)) + { + return; + } + + spinner.Animation = new Int32Animation(spinner.FragmentCount, new Duration(spinner.Delay)) + { + RepeatBehavior = RepeatBehavior.Forever + }; + } + #endregion + #region DependencyProperty: DistanceMultiplier [AffectsRender] (System.Double) + public static readonly DependencyProperty DistanceMultiplierProperty = DependencyProperty.Register( + "DistanceMultiplier", + typeof(double), + typeof(ProgressSpinner), + new FrameworkPropertyMetadata(12.0, FrameworkPropertyMetadataOptions.AffectsRender) + ); + public double DistanceMultiplier + { + get => (double)this.GetValue(DistanceMultiplierProperty); + set => this.SetValue(DistanceMultiplierProperty, value); + } + #endregion + #region DependencyProperty: CurrentFragment [AffectsRender] (System.Int32) + public static readonly DependencyProperty CurrentFragmentProperty = DependencyProperty.Register( + "CurrentFragment", + typeof(int), + typeof(ProgressSpinner), + new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsRender) + ); + public int CurrentFragment + { + get => (int)this.GetValue(CurrentFragmentProperty); + set => this.SetValue(CurrentFragmentProperty, value); + } + #endregion + #region DependencyProperty: IsAnimating [PropertyChangedCallback, AffectsRender] (System.Boolean) + public static readonly DependencyProperty IsAnimatingProperty = DependencyProperty.Register( + "IsAnimating", + typeof(bool), + typeof(ProgressSpinner), + new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender, IsAnimating_PropertyChangedCallback) + ); + public bool IsAnimating + { + get => (bool)this.GetValue(IsAnimatingProperty); + set => this.SetValue(IsAnimatingProperty, value); + } + + private static void IsAnimating_PropertyChangedCallback(DependencyObject target, DependencyPropertyChangedEventArgs e) + { + if (!(target is ProgressSpinner spinner)) + { + return; + } + + if (spinner.Animation == null) + { + spinner.Animation = new Int32Animation(spinner.FragmentCount, new Duration(spinner.Delay)) + { + RepeatBehavior = RepeatBehavior.Forever + }; + } + spinner.BeginAnimation(CurrentFragmentProperty, (e.NewValue == null ? false : (bool)e.NewValue) ? spinner.Animation : null); + } + #endregion + #region DependencyProperty: Delay [PropertyChangedCallback, AffectsRender] (System.TimeSpan) + public static readonly DependencyProperty DelayProperty = DependencyProperty.Register( + nameof(Delay), + typeof(TimeSpan), + typeof(ProgressSpinner), + new FrameworkPropertyMetadata(new TimeSpan(0, 0, 0, 0, 750), FrameworkPropertyMetadataOptions.AffectsRender, Delay_PropertyChangedCallback) + ); + public TimeSpan Delay + { + get => (TimeSpan)this.GetValue(DelayProperty); + set => this.SetValue(IsAnimatingProperty, value); + } + private static void Delay_PropertyChangedCallback(DependencyObject target, DependencyPropertyChangedEventArgs e) + { + if (!(target is ProgressSpinner spinner)) + { + return; + } + spinner.Animation = new Int32Animation(spinner.FragmentCount, new Duration(spinner.Delay)) + { + RepeatBehavior = RepeatBehavior.Forever + }; + + } + #endregion + + + public Int32Animation Animation { get; private set; } + + + public ProgressSpinner() + { + } + + protected override void OnRender(DrawingContext drawingContext) + { + double heightHalved = this.RenderSize.Height / 2; + double widthHalved = this.RenderSize.Width / 2; + var fragmentActive = this.FragmentActive; + var fragmentInactive = this.FragmentInactive; + int fragmentBorderThickness = this.FragmentBorderThickness; + var fragmentBorderFill = this.FragmentBorderFill; + int fragmentCount = this.FragmentCount; + int currentFragment = this.CurrentFragment; + bool isAnimating = this.IsAnimating; + + double innerDistance = this.InnerDistance; + double outerDistance = this.OuterDistance; + var pen = new Pen(fragmentBorderFill, fragmentBorderThickness); + pen.Freeze(); + + double degreePerFragment = 360 / this.DistanceMultiplier; + double radPerFragment = Math.PI * degreePerFragment / 180.0; + + var vectorBotRight = new Vector(Math.Cos(radPerFragment / 2), Math.Sin(radPerFragment / 2)); + var vectorBotLeft = new Vector(Math.Cos(-(radPerFragment / 2)), Math.Sin(-(radPerFragment / 2))); + + var vectorTopRight = vectorBotRight * (1 - outerDistance); + var vectorTopLeft = vectorBotLeft * (1 - outerDistance); + + var fragmentInactiveBrush = new SolidColorBrush(fragmentInactive); + fragmentInactiveBrush.Freeze(); + + vectorBotRight *= innerDistance; + vectorBotLeft *= innerDistance; + + vectorBotRight *= widthHalved; + vectorBotLeft *= widthHalved; + + vectorTopRight *= widthHalved; + vectorTopLeft *= widthHalved; + for (int i = 0; i < fragmentCount; i++) + { + var geo = new StreamGeometry(); + using (var geoContext = geo.Open()) + { + geoContext.BeginFigure(new Point(vectorBotLeft.X, vectorBotLeft.Y), true, false); + geoContext.LineTo(new Point(vectorBotRight.X, vectorBotRight.Y), false, false); + geoContext.LineTo(new Point(vectorTopRight.X, vectorTopRight.Y), false, false); + geoContext.LineTo(new Point(vectorTopLeft.X, vectorTopLeft.Y), false, false); + } + var transformgroup = new TransformGroup(); + var rotTransform = new RotateTransform(360 / fragmentCount * i); + rotTransform.Freeze(); + transformgroup.Children.Add(rotTransform); + var locTransform = new TranslateTransform(widthHalved, heightHalved); + locTransform.Freeze(); + transformgroup.Children.Add(locTransform); + transformgroup.Freeze(); + geo.Transform = transformgroup; + geo.Freeze(); + if (isAnimating) + { + double leftStrength, rightStrength; + if (i >= currentFragment) + { + leftStrength = (double)(fragmentCount - Math.Abs(i - currentFragment)) / fragmentCount; + rightStrength = 1 - (double)(fragmentCount - Math.Abs(i - currentFragment)) / fragmentCount; + } + else + { + leftStrength = 1 - (double)(fragmentCount - Math.Abs(i - currentFragment)) / fragmentCount; + rightStrength = (double)(fragmentCount - Math.Abs(i - currentFragment)) / fragmentCount; + } + var brush = new SolidColorBrush(Color.FromArgb( + (byte)(leftStrength * fragmentInactive.A + rightStrength * fragmentActive.A), + (byte)(leftStrength * fragmentInactive.R + rightStrength * fragmentActive.R), + (byte)(leftStrength * fragmentInactive.G + rightStrength * fragmentActive.G), + (byte)(leftStrength * fragmentInactive.B + rightStrength * fragmentActive.B) + )); + brush.Freeze(); + drawingContext.DrawGeometry(brush, pen, geo); + } + else + { + drawingContext.DrawGeometry(fragmentInactiveBrush, pen, geo); + } + } + base.OnRender(drawingContext); + } + } +} diff --git a/Arma.Studio.Data/UI/ProgressSpinner.xaml b/Arma.Studio.Data/UI/ProgressSpinner.xaml new file mode 100644 index 0000000..ca135d8 --- /dev/null +++ b/Arma.Studio.Data/UI/ProgressSpinner.xaml @@ -0,0 +1,13 @@ + + + \ No newline at end of file diff --git a/Arma.Studio/App.xaml b/Arma.Studio/App.xaml index 287e270..d21da02 100644 --- a/Arma.Studio/App.xaml +++ b/Arma.Studio/App.xaml @@ -26,6 +26,7 @@ + @@ -63,6 +64,28 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/Arma.Studio/UI/Windows/MainWindow.xaml b/Arma.Studio/UI/Windows/MainWindow.xaml index de1d4d7..ebdc358 100644 --- a/Arma.Studio/UI/Windows/MainWindow.xaml +++ b/Arma.Studio/UI/Windows/MainWindow.xaml @@ -524,6 +524,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +