diff --git a/src/Files.App.Controls/ThemedIcon/Data/ThemedIconColorType.cs b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconColorType.cs new file mode 100644 index 000000000000..f4f4c67c2da4 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconColorType.cs @@ -0,0 +1,50 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.Controls +{ + /// + /// Defines the IconColorTypes for which sets the visual state + /// to use the correct brush values which match system signal colors. + /// + public enum ThemedIconColorType + { + None, + + /// + /// Icon color type of is Normal. Default Value. + /// + Normal, + + /// + /// Icon color type of is Critical. + /// + Critical, + + /// + /// Icon color type of is Caution. + /// + Caution, + + /// + /// Icon color type of is Success. + /// + Success, + + /// + /// Icon color type of is Neutral. + /// + Neutral, + + /// + /// Icon color type of is Accent. + /// + Accent, + + /// + /// Icon color type of is Custom. Used in combination + /// with the IconColor and Foreground brushes. + /// + Custom + } +} diff --git a/src/Files.App.Controls/ThemedIcon/Data/ThemedIconLayerType.cs b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconLayerType.cs new file mode 100644 index 000000000000..ac4ef314b8d6 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconLayerType.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.Controls +{ + /// + /// Defines LayerTypes for . + /// + public enum ThemedIconLayerType + { + /// + /// The LayerType uses the Base color. Default state. + /// + Base, + /// + /// The LayerType uses the Alt color. + /// + Alt, + /// + /// The LayerType uses an Accented color. + /// + Accent, + /// + /// The LayerType uses a contrasting color for the Accented color. + /// + AccentContrast, + } +} diff --git a/src/Files.App.Controls/ThemedIcon/Data/ThemedIconLayers.cs b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconLayers.cs new file mode 100644 index 000000000000..653670046591 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconLayers.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.Controls +{ + /// + /// A collection of Layers for the ThemedIcon's Layered IconType + /// + public sealed class ThemedIconLayers : List + { + } +} diff --git a/src/Files.App.Controls/ThemedIcon/Data/ThemedIconToggleBehaviors.cs b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconToggleBehaviors.cs new file mode 100644 index 000000000000..3490f15a1da4 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconToggleBehaviors.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.Controls +{ + /// + /// Defines IconTypes for . + /// + public enum ToggleBehaviors + { + /// + /// Auto enables the ThemedIcon to listen to owner control states. + /// + Auto, + + /// + /// On will always use the ThemedIcon's Toggle state + /// + On, + + /// + /// Off will not use the ThemedIcon's Toggle state + /// + Off, + } +} diff --git a/src/Files.App.Controls/ThemedIcon/Data/ThemedIconTypes.cs b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconTypes.cs new file mode 100644 index 000000000000..58299f1d61fd --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconTypes.cs @@ -0,0 +1,21 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.Controls +{ + /// + /// Defines IconTypes for . + /// + public enum ThemedIconTypes + { + /// + /// Icon type of is Outline. + /// + Outline, + + /// + /// Icon type of is Layered. + /// + Layered, + } +} diff --git a/src/Files.App.Controls/ThemedIcon/ThemedIcon.Consts.cs b/src/Files.App.Controls/ThemedIcon/ThemedIcon.Consts.cs index fb3388f3c4c1..b674c25d1432 100644 --- a/src/Files.App.Controls/ThemedIcon/ThemedIcon.Consts.cs +++ b/src/Files.App.Controls/ThemedIcon/ThemedIcon.Consts.cs @@ -6,69 +6,65 @@ namespace Files.App.Controls { - // Template Parts - [TemplatePart(Name = FilledPathIconViewBox, Type = typeof(Viewbox))] - [TemplatePart(Name = OutlinePathIconViewBox, Type = typeof(Viewbox))] - [TemplatePart(Name = LayeredPathIconViewBox, Type = typeof(Viewbox))] - [TemplatePart(Name = LayeredPathCanvas, Type = typeof(Canvas))] - - // Icon Type Visual States - [TemplateVisualState(Name = OutlineTypeStateName, GroupName = IconTypeStateGroupName)] - [TemplateVisualState(Name = LayeredTypeStateName, GroupName = IconTypeStateGroupName)] - [TemplateVisualState(Name = FilledTypeStateName, GroupName = IconTypeStateGroupName)] + // Template Parts + [TemplatePart(Name = FilledPathIconViewBox, Type = typeof(Viewbox))] + [TemplatePart(Name = OutlinePathIconViewBox, Type = typeof(Viewbox))] + [TemplatePart(Name = LayeredPathIconViewBox, Type = typeof(Viewbox))] + [TemplatePart(Name = LayeredPathCanvas, Type = typeof(Canvas))] + // Icon Type Visual States + [TemplateVisualState(Name = OutlineTypeStateName, GroupName = IconTypeStateGroupName)] + [TemplateVisualState(Name = LayeredTypeStateName, GroupName = IconTypeStateGroupName)] + [TemplateVisualState(Name = FilledTypeStateName, GroupName = IconTypeStateGroupName)] + // Icon Color Visual States + [TemplateVisualState(Name = NormalStateName, GroupName = IconColorStateGroupName)] + [TemplateVisualState(Name = CriticalStateName, GroupName = IconColorStateGroupName)] + [TemplateVisualState(Name = CautionStateName, GroupName = IconColorStateGroupName)] + [TemplateVisualState(Name = SuccessStateName, GroupName = IconColorStateGroupName)] + [TemplateVisualState(Name = NeutralStateName, GroupName = IconColorStateGroupName)] + [TemplateVisualState(Name = AccentStateName, GroupName = IconColorStateGroupName)] + [TemplateVisualState(Name = CustomColorStateName, GroupName = IconColorStateGroupName)] + [TemplateVisualState(Name = ToggleStateName, GroupName = IconColorStateGroupName)] + [TemplateVisualState(Name = DisabledColorStateName, GroupName = IconColorStateGroupName)] + [TemplateVisualState(Name = DisabledToggleColorStateName, GroupName = IconColorStateGroupName)] + // Icon IsEnabled Visual States + [TemplateVisualState(Name = EnabledStateName, GroupName = EnabledStateGroupName)] + [TemplateVisualState(Name = DisabledStateName, GroupName = EnabledStateGroupName)] + public partial class ThemedIcon + { + // Visual State Group Names + internal const string IconTypeStateGroupName = "IconTypeStates"; + internal const string IconColorStateGroupName = "IconColorStates"; + internal const string EnabledStateGroupName = "EnabledStates"; - // Icon Color Visual States - [TemplateVisualState(Name = NormalStateName, GroupName = IconColorStateGroupName)] - [TemplateVisualState(Name = CriticalStateName, GroupName = IconColorStateGroupName)] - [TemplateVisualState(Name = CautionStateName, GroupName = IconColorStateGroupName)] - [TemplateVisualState(Name = SuccessStateName, GroupName = IconColorStateGroupName)] - [TemplateVisualState(Name = NeutralStateName, GroupName = IconColorStateGroupName)] - [TemplateVisualState(Name = AccentStateName, GroupName = IconColorStateGroupName)] - [TemplateVisualState(Name = CustomColorStateName, GroupName = IconColorStateGroupName)] - [TemplateVisualState(Name = ToggleStateName, GroupName = IconColorStateGroupName)] - [TemplateVisualState(Name = DisabledColorStateName, GroupName = IconColorStateGroupName)] - [TemplateVisualState(Name = DisabledToggleColorStateName, GroupName = IconColorStateGroupName)] + // "Icon Type" Visual State Names + internal const string OutlineTypeStateName = "Outline"; + internal const string FilledTypeStateName = "Filled"; + internal const string LayeredTypeStateName = "Layered"; - // Icon IsEnabled Visual States - [TemplateVisualState(Name = EnabledStateName, GroupName = EnabledStateGroupName)] - [TemplateVisualState(Name = DisabledStateName, GroupName = EnabledStateGroupName)] + // "Icon State" Visual State Names + internal const string NormalStateName = "Normal"; + internal const string CriticalStateName = "Critical"; + internal const string CautionStateName = "Caution"; + internal const string SuccessStateName = "Success"; + internal const string NeutralStateName = "Neutral"; + internal const string AccentStateName = "Accent"; + internal const string CustomColorStateName = "Custom"; + internal const string ToggleStateName = "Toggle"; + internal const string DisabledColorStateName = "DisabledColor"; + internal const string DisabledToggleColorStateName = "DisabledToggleColor"; - public partial class ThemedIcon - { - // Visual State Group Names - internal const string IconTypeStateGroupName = "IconTypeStates"; - internal const string IconColorStateGroupName = "IconColorStates"; - internal const string EnabledStateGroupName = "EnabledStates"; + // "Enabled" Visual State Names + internal const string EnabledStateName = "Enabled"; + internal const string DisabledStateName = "Disabled"; - // "Icon Type" Visual State Names - internal const string OutlineTypeStateName = "Outline"; - internal const string FilledTypeStateName = "Filled"; - internal const string LayeredTypeStateName = "Layered"; + // ViewBox Controls + internal const string FilledPathIconViewBox = "PART_FilledIconViewBox"; + internal const string OutlinePathIconViewBox = "PART_OutlineIconViewBox"; + internal const string LayeredPathIconViewBox = "PART_LayeredIconViewBox"; + internal const string LayeredPathCanvas = "PART_LayerCanvas"; - // "Icon State" Visual State Names - internal const string NormalStateName = "Normal"; - internal const string CriticalStateName = "Critical"; - internal const string CautionStateName = "Caution"; - internal const string SuccessStateName = "Success"; - internal const string NeutralStateName = "Neutral"; - internal const string AccentStateName = "Accent"; - internal const string CustomColorStateName = "Custom"; - internal const string ToggleStateName = "Toggle"; - internal const string DisabledColorStateName = "DisabledColor"; - internal const string DisabledToggleColorStateName = "DisabledToggleColor"; - - // "Enabled" Visual State Names - internal const string EnabledStateName = "Enabled"; - internal const string DisabledStateName = "Disabled"; - - // ViewBox Controls - internal const string FilledPathIconViewBox = "PART_FilledIconViewBox"; - internal const string OutlinePathIconViewBox = "PART_OutlineIconViewBox"; - internal const string LayeredPathIconViewBox = "PART_LayeredIconViewBox"; - internal const string LayeredPathCanvas = "PART_LayerCanvas"; - - // Path Controls - internal const string FilledIconPath = "PART_FilledPath"; - internal const string OutlineIconPath = "PART_OutlinePath"; - } -} \ No newline at end of file + // Path Controls + internal const string FilledIconPath = "PART_FilledPath"; + internal const string OutlineIconPath = "PART_OutlinePath"; + } +} diff --git a/src/Files.App.Controls/ThemedIcon/ThemedIcon.Owner.cs b/src/Files.App.Controls/ThemedIcon/ThemedIcon.Owner.cs new file mode 100644 index 000000000000..95770e40025c --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/ThemedIcon.Owner.cs @@ -0,0 +1,62 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +using CommunityToolkit.WinUI.UI; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; + +namespace Files.App.Controls +{ + public partial class ThemedIcon + { + private bool _isOwnerToggled; + private bool _isOwnerEnabled; + + private Control? ownerControl = null; + private ToggleButton? ownerToggleButton = null; + + private void FindOwnerControlStates() + { + if (this.FindAscendant() is ToggleButton toggleButton) + { + ownerToggleButton = toggleButton; + + // IsChecked/IsToggled change aware + ownerToggleButton.Checked += OwnerControl_IsCheckedChanged; + ownerToggleButton.Unchecked += OwnerControl_IsCheckedChanged; + _isOwnerToggled = ownerToggleButton.IsChecked is true; + } + + if (this.FindAscendant() is Control control) + { + ownerControl = control; + + // IsEnabled change aware + ownerControl.IsEnabledChanged += OwnerControl_IsEnabledChanged; + _isOwnerEnabled = ownerControl.IsEnabled; + + UpdateVisualStates(); + } + } + + private void OwnerControl_IsCheckedChanged(object sender, RoutedEventArgs e) + { + if (ownerToggleButton is null) + return; + + _isOwnerToggled = ownerToggleButton.IsChecked is true; + UpdateVisualStates(); + + } + + private void OwnerControl_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) + { + if (ownerControl is null) + return; + + _isOwnerEnabled = ownerControl.IsEnabled; + UpdateVisualStates(); + } + } +} diff --git a/src/Files.App.Controls/ThemedIcon/ThemedIcon.Properties.cs b/src/Files.App.Controls/ThemedIcon/ThemedIcon.Properties.cs index 0562d2409f0e..a6c1d8ecc5c5 100644 --- a/src/Files.App.Controls/ThemedIcon/ThemedIcon.Properties.cs +++ b/src/Files.App.Controls/ThemedIcon/ThemedIcon.Properties.cs @@ -16,56 +16,62 @@ namespace Files.App.Controls [DependencyProperty("IsFilled", nameof(OnIsFilledPropertyChanged), DefaultValue = "false")] [DependencyProperty("IsHighContrast", nameof(OnIsHighContrastPropertyChanged), DefaultValue = "false")] [DependencyProperty("Layers", nameof(OnLayersPropertyChanged))] + [DependencyProperty("ToggleBehavior", nameof(OnToggleBehaviorPropertyChanged), DefaultValue = "ToggleBehaviors.Auto")] public partial class ThemedIcon : Control { protected virtual void OnFilledIconPropertyChanged(string oldValue, string newValue) { - UpdateFilledIconPath(); + OnFilledIconChanged(); } protected virtual void OnOutlineIconPropertyChanged(string oldValue, string newValue) { - UpdateOutlineIconPath(); + OnOutlineIconChanged(); } protected virtual void OnColorPropertyChanged(Brush oldValue, Brush newValue) { - UpdateIconTypeStates(); + OnIconTypeChanged(); } protected virtual void OnIconTypePropertyChanged(ThemedIconTypes oldValue, ThemedIconTypes newValue) { - UpdateIconTypeStates(); + OnIconTypeChanged(); } protected virtual void OnIconColorTypePropertyChanged(ThemedIconColorType oldValue, ThemedIconColorType newValue) { - UpdateIconColorTypeStates(); + OnIconColorTypeChanged(); } protected virtual void OnIconSizePropertyChanged(double oldValue, double newValue) { - IconSizePropertyChanged(newValue); + UpdateVisualStates(); } protected virtual void OnIsToggledPropertyChanged(bool oldValue, bool newValue) { - ToggleChanged(newValue); + UpdateVisualStates(); } protected virtual void OnIsFilledPropertyChanged(bool oldValue, bool newValue) { - FilledChanged(newValue); + UpdateVisualStates(); } protected virtual void OnIsHighContrastPropertyChanged(bool oldValue, bool newValue) { - HighContrastChanged(newValue); + UpdateVisualStates(); } protected virtual void OnLayersPropertyChanged(object oldValue, object newValue) { - UpdateLayeredIconContent(); + UpdateVisualStates(); + } + + protected virtual void OnToggleBehaviorPropertyChanged(ToggleBehaviors oldValue, ToggleBehaviors newValue) + { + UpdateVisualStates(); } } } diff --git a/src/Files.App.Controls/ThemedIcon/ThemedIcon.ThemeResources.xaml b/src/Files.App.Controls/ThemedIcon/ThemedIcon.ThemeResources.xaml new file mode 100644 index 000000000000..3cd7ec2c26ba --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/ThemedIcon.ThemeResources.xaml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + #DBF0F0F0 + #66161616 + + + + + + + + + + + + + + + + + + + False + + + + + + #FF000000 + #00000000 + + + + + + + + + + + + + + + + + + + True + + + + + + #DB161616 + #66F0F0F0 + + + + + + + + + + + + + + + + + + + False + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/ThemedIcon.cs b/src/Files.App.Controls/ThemedIcon/ThemedIcon.cs index be5b0d031f22..57a14dc3d81f 100644 --- a/src/Files.App.Controls/ThemedIcon/ThemedIcon.cs +++ b/src/Files.App.Controls/ThemedIcon/ThemedIcon.cs @@ -6,371 +6,239 @@ using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Markup; using Microsoft.UI.Xaml.Shapes; -using CommunityToolkit.WinUI.UI; -using Microsoft.UI.Xaml.Controls.Primitives; using System.Linq; using System.Collections.Generic; -using System.Reflection.Emit; namespace Files.App.Controls { - /// - /// A control for a State and Color aware Icon - /// - public partial class ThemedIcon : Control - { - private bool _isHighContrast; - private bool _isToggled; - private bool _isEnabled; - private bool _isFilled; - private double _iconSize; - - private ToggleButton? ownerToggleButton = null; - private AppBarToggleButton? ownerAppBarToggleButton = null; - private Control? ownerControl = null; - - public ThemedIcon() - { - DefaultStyleKey = typeof(ThemedIcon); - } - - protected override void OnApplyTemplate() - { - IsEnabledChanged -= OnIsEnabledChanged; - SizeChanged -= OnSizeChanged; - - base.OnApplyTemplate(); - - IsEnabledChanged += OnIsEnabledChanged; - SizeChanged += OnSizeChanged; - - InitialIconStateValues(); - FindOwnerControlStates(); - UpdateIconContent(); - UpdateIconStates(); - UpdateVisualStates(); - } - - private void UpdateIconContent() - { - // Updates PathData and Layers - UpdateFilledIconPath(); - UpdateOutlineIconPath(); - UpdateLayeredIconContent(); - } - - private void UpdateFilledIconPath() - { - // Updates Filled Icon from Path Data - if (GetTemplateChild(FilledPathIconViewBox) is not Viewbox filledViewBox) - return; - - SetPathData(FilledIconPath, FilledIconData ?? string.Empty, filledViewBox); - } - - private void UpdateOutlineIconPath() - { - // Updates Outline Icon from Path Data - if (GetTemplateChild(OutlinePathIconViewBox) is not Viewbox outlineViewBox) - return; - - SetPathData(OutlineIconPath, OutlineIconData ?? string.Empty, outlineViewBox); - } - - private void UpdateLayeredIconContent() - { - // Updates Layered Icon from it's Layers - if (GetTemplateChild(LayeredPathIconViewBox) is not Viewbox layeredViewBox || - GetTemplateChild(LayeredPathCanvas) is not Canvas canvas || - Layers is not ICollection layers) - return; - - canvas.Children.Clear(); - - foreach (var layer in layers) - { - canvas.Children.Add( - new ThemedIconLayer() - { - LayerType = layer.LayerType, - IconColorType = layer.IconColorType, - PathData = layer.PathData, - Opacity = layer.Opacity, - LayerColor = this.Color, - Foreground = this.Foreground, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - LayerSize = _iconSize, - Width = layer.LayerSize, - Height = layer.LayerSize - - }); - } - } - - private void SetPathData(string partName, string pathData, FrameworkElement element) - { - // Updates PathData - if (string.IsNullOrEmpty(pathData)) - return; - - var geometry = (Geometry)XamlReader.Load( - $"{pathData}"); - - if (GetTemplateChild(partName) is Path path) - { - path.Data = geometry; - path.Width = _iconSize; - path.Height = _iconSize; - } - } - - private void FindOwnerControlStates() - { - /* - // Finds the owner Control and it's Checked and Enabled state - - // - // Check if owner Control is a ToggleButton - // Hooks onto Event handlers when IsChecked and IsUnchecked runs - // Runs the ToggleChanged event, to set initial value, if the ToggleButton's isChecked is true - // - // Check if owner Control is an AppBarToggleButton - // Hooks onto Event handlers when IsChecked and IsUnchecked runs - // Runs the ToggleChanged event, to set initial value, if the AppBarToggleButton's isChecked is true - // - // Gets the owner Control - // Hooks onto Event handlers when IsEnabledChanged runs - // Runs the EnabledChanged event to set initial value - // - */ - - ownerToggleButton = this.FindAscendant(); - - if (ownerToggleButton != null) - { - ownerToggleButton.Checked += OwnerControl_IsCheckedChanged; - ownerToggleButton.Unchecked += OwnerControl_IsCheckedChanged; - - ToggleChanged(ownerToggleButton.IsChecked is true); - } - - ownerAppBarToggleButton = this.FindAscendant(); - - if (ownerAppBarToggleButton != null) - { - ownerAppBarToggleButton.Checked += OwnerControl_IsCheckedChanged; - ownerAppBarToggleButton.Unchecked += OwnerControl_IsCheckedChanged; - - ToggleChanged(ownerAppBarToggleButton.IsChecked is true); - } - - ownerControl = this.FindAscendant(); - - if (ownerControl != null) - { - ownerControl.IsEnabledChanged += OwnerControl_IsEnabledChanged; - - EnabledChanged(ownerControl.IsEnabled); - } - } - - private void OwnerControl_IsCheckedChanged(object sender, RoutedEventArgs e) - { - // Responds to owner checked changes - if (ownerToggleButton is null && ownerAppBarToggleButton is null) - return; - - if (ownerToggleButton is not null) - ToggleChanged(ownerToggleButton.IsChecked is true); - else if (ownerAppBarToggleButton is not null) - ToggleChanged(ownerAppBarToggleButton.IsChecked is true); - } - - private void OwnerControl_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) - { - // Responds to owner control enabled changes - if (ownerControl is null) - return; - - EnabledChanged(ownerControl.IsEnabled); - } - - private void ToggleChanged(bool value) - { - // Handles the IsToggled property change - _isToggled = value; - - UpdateVisualStates(); - } - - private void FilledChanged(bool value) - { - // Handles the IsToggled property change - _isFilled = value; - - UpdateVisualStates(); - } - - private void OnIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) - { - // Handles for the derived control's IsEnabled property change - EnabledChanged((bool)e.NewValue); - } - - private void OnSizeChanged(object sender, SizeChangedEventArgs e) - { - // Handles resizing of child layers when Width and Height properties change - } - - private void IconSizePropertyChanged(double value) - { - // Code to handle the design time Icon Size changing - _iconSize = value; - - UpdateVisualStates(); - } - - - private void EnabledChanged(bool value) - { - // Handles the IsEnabled property change - _isEnabled = value; - - UpdateVisualStates(); - } - - private void ThemeSettings_OnHighContrastChanged(object sender, bool e) - { - HighContrastChanged(e); - } - - private void HighContrastChanged(bool value) - { - // handles HighContrast property change - _isHighContrast = value; - - UpdateVisualStates(); - } - - private void InitialIconStateValues() - { - _isEnabled = IsEnabled; - _isToggled = IsToggled; - _isHighContrast = IsHighContrast; - _iconSize = IconSize; - } - - private void UpdateIconStates() - { - ToggleChanged(_isToggled); - EnabledChanged(_isEnabled); - HighContrastChanged(_isHighContrast); - } - - private void UpdateVisualStates() - { - // Updates all Icon Visual States. - UpdateIconTypeStates(); - UpdateIconColorTypeStates(); - } - - private void UpdateIconTypeStates() - { - /* - // Handles changes to the IconType and setting the correct Visual States. - - // Handles the two IconType states, based on the ThemedIcon.IconType value - // as well as states derived from owner controls, and other properties - - // We first check for isToggled and isFilled icon types and states - // Then we check for Contrast and Disabled states, to replace Layered with Outline and set EnabledStates - // Finally we assigned Filled and Layered states, and default otherwise to Outline - */ - - if (_isToggled is true || IsToggled is true || _isFilled is true || IsFilled is true) - { - VisualStateManager.GoToState(this, FilledTypeStateName, true); - return; - } - else if (_isHighContrast is true || IsHighContrast is true || _isEnabled is false || IsEnabled is false) - { - VisualStateManager.GoToState(this, OutlineTypeStateName, true); - VisualStateManager.GoToState(this, DisabledStateName, true); - return; - } - else - { - if (IconType == ThemedIconTypes.Layered) - { - VisualStateManager.GoToState(this, LayeredTypeStateName, true); - } - else - { - VisualStateManager.GoToState(this, OutlineTypeStateName, true); - } - } - - VisualStateManager.GoToState(this, EnabledStateName, true); - } - - private void UpdateIconColorTypeStates() - { - /* - // Handles changes to the IconColorType and setting the correct Visual States. - - // We first check if the Icon is Disabled - // Then we check if the Disabled Icon is Toggled - - // We then assume the Icon is Enabled - // We then check the Toggled state for the Contrast Icons - // We have two states depending on toggle. - - // Finally we act on all other Enabled states - // We check for Toggled state - // And update the IconColorType in the Layered Icon's Layers - */ - - if (_isEnabled is false || IsEnabled is false) - { - if (_isToggled is true || IsToggled is true) - { - VisualStateManager.GoToState(this, DisabledToggleColorStateName, true); - } - else - { - VisualStateManager.GoToState(this, DisabledColorStateName, true); - } - } - else - { - if (_isToggled is true || IsToggled is true) - { - VisualStateManager.GoToState(this, ToggleStateName, true); - } - else - { - VisualStateManager.GoToState( - this, - IconColorType switch - { - ThemedIconColorType.Critical => CriticalStateName, - ThemedIconColorType.Caution => CautionStateName, - ThemedIconColorType.Success => SuccessStateName, - ThemedIconColorType.Neutral => NeutralStateName, - ThemedIconColorType.Accent => AccentStateName, - ThemedIconColorType.Custom => CustomColorStateName, - _ => NormalStateName, - }, - true); - } - - if (GetTemplateChild(LayeredPathCanvas) is Canvas canvas) - { - foreach (var layer in canvas.Children.Cast()) - layer.IconColorType = IconColorType; - } - } - } - } + /// + /// A control for a State and Color aware Icon + /// + public partial class ThemedIcon : Control + { + private Viewbox? _filledViewBox; + private Viewbox? _outlineViewBox; + private Viewbox? _layeredViewBox; + private Canvas? _layeredCanvas; + + public ThemedIcon() + { + DefaultStyleKey = typeof(ThemedIcon); + } + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + IsEnabledChanged += OnIsEnabledChanged; + + _isOwnerEnabled = IsEnabled; + + GetTemplateParts(); + + FindOwnerControlStates(); + OnFilledIconChanged(); + OnOutlineIconChanged(); + OnLayeredIconChanged(); + + OnIconTypeChanged(); + OnIconColorTypeChanged(); + } + + private void GetTemplateParts() + { + // Gets the template parts and sets the private fields + _outlineViewBox = GetTemplateChild( OutlinePathIconViewBox ) as Viewbox; + _filledViewBox = GetTemplateChild( FilledPathIconViewBox ) as Viewbox; + _layeredViewBox = GetTemplateChild( LayeredPathIconViewBox ) as Viewbox; + + _layeredCanvas = GetTemplateChild( LayeredPathCanvas ) as Canvas; + } + + // Updates paths and layers + + private void OnFilledIconChanged() + { + // Updates Filled Icon from Path Data + if (_filledViewBox == null) + return; + + SetPathData(FilledIconPath, FilledIconData ?? string.Empty, _filledViewBox ); + } + + private void OnOutlineIconChanged() + { + // Updates Outline Icon from Path Data + if (_outlineViewBox == null) + return; + + SetPathData(OutlineIconPath, OutlineIconData ?? string.Empty, _outlineViewBox ); + } + + private void OnLayeredIconChanged() + { + // Updates Layered Icon from it's Layers + if ( _layeredViewBox == null || + _layeredCanvas == null || + Layers is not ICollection layers) + return; + + _layeredCanvas.Children.Clear(); + + foreach (var layer in layers) + { + _layeredCanvas.Children.Add( + new ThemedIconLayer() + { + LayerType = layer.LayerType, + IconColorType = layer.IconColorType, + PathData = layer.PathData, + Opacity = layer.Opacity, + LayerColor = Color, + Foreground = Foreground, + HorizontalAlignment = HorizontalAlignment.Stretch, + VerticalAlignment = VerticalAlignment.Stretch, + LayerSize = IconSize, + Width = layer.LayerSize, + Height = layer.LayerSize + + }); + } + } + + // Updates visual states + + private void OnIconTypeChanged() + { + switch (ToggleBehavior) + { + case ToggleBehaviors.Auto: + { + if (_isOwnerToggled is true || IsFilled is true) + { + VisualStateManager.GoToState(this, FilledTypeStateName, true); + return; + } + else if (IsHighContrast is true || _isOwnerEnabled is false || IsEnabled is false) + { + VisualStateManager.GoToState(this, OutlineTypeStateName, true); + VisualStateManager.GoToState(this, DisabledStateName, true); + return; + } + else + { + VisualStateManager.GoToState( + this, + IconType is ThemedIconTypes.Layered ? LayeredTypeStateName : OutlineTypeStateName, + true); + } + } + break; + case ToggleBehaviors.Off: + { + if (IsFilled is true) + { + VisualStateManager.GoToState(this, FilledTypeStateName, true); + return; + } + else if (IsHighContrast is true || _isOwnerEnabled is false || IsEnabled is false) + { + VisualStateManager.GoToState(this, OutlineTypeStateName, true); + VisualStateManager.GoToState(this, DisabledStateName, true); + return; + } + else + { + VisualStateManager.GoToState( + this, + IconType is ThemedIconTypes.Layered ? LayeredTypeStateName : OutlineTypeStateName, + true); + } + } + break; + case ToggleBehaviors.On: + { + VisualStateManager.GoToState(this, FilledTypeStateName, true); + } + break; + } + + VisualStateManager.GoToState(this, EnabledStateName, true); + } + + private void OnIconColorTypeChanged() + { + if (_isOwnerEnabled && IsEnabled) + { + if ((ToggleBehavior is ToggleBehaviors.Auto && _isOwnerToggled) || + ToggleBehavior is ToggleBehaviors.On) + { + // Toggle + VisualStateManager.GoToState(this, ToggleStateName, true); + } + else + { + // Use colorful ones + VisualStateManager.GoToState( + this, + IconColorType switch + { + ThemedIconColorType.Critical => CriticalStateName, + ThemedIconColorType.Caution => CautionStateName, + ThemedIconColorType.Success => SuccessStateName, + ThemedIconColorType.Neutral => NeutralStateName, + ThemedIconColorType.Accent => AccentStateName, + ThemedIconColorType.Custom => CustomColorStateName, + _ => NormalStateName, + }, + true); + } + + // Update layered icon color + if (_layeredCanvas != null) + foreach (var layer in _layeredCanvas.Children.Cast()) + layer.IconColorType = IconColorType; + } + else + { + // Disable + toggle + if ((ToggleBehavior is ToggleBehaviors.Auto && _isOwnerToggled is true) || + ToggleBehavior is ToggleBehaviors.On) + VisualStateManager.GoToState(this, DisabledToggleColorStateName, true); + // Disable + else + VisualStateManager.GoToState(this, DisabledColorStateName, true); + } + } + + // Misc + + private void UpdateVisualStates() + { + OnIconTypeChanged(); + OnIconColorTypeChanged(); + } + + private void SetPathData(string partName, string pathData, FrameworkElement element) + { + // Updates PathData + if (string.IsNullOrEmpty(pathData)) + return; + + var geometry = (Geometry)XamlReader.Load( + $"{pathData}"); + + if (GetTemplateChild(partName) is Path path) + { + path.Data = geometry; + path.Width = IconSize; + path.Height = IconSize; + } + } + + private void OnIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) + { + UpdateVisualStates(); + } + } } diff --git a/src/Files.App.Controls/ThemedIcon/ThemedIcon.xaml b/src/Files.App.Controls/ThemedIcon/ThemedIcon.xaml index 7cbdc9468e37..9da6cbf6e9c7 100644 --- a/src/Files.App.Controls/ThemedIcon/ThemedIcon.xaml +++ b/src/Files.App.Controls/ThemedIcon/ThemedIcon.xaml @@ -4,6 +4,8 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Files.App.Controls"> + - + + + + + + + @@ -34,19 +69,26 @@ + - + - + + + + + - + + + @@ -56,501 +98,985 @@ - - + + + + + + Grid.Column="1" + Style="{StaticResource TextExampleCaptionStyle}" + Text="Layered" /> - - - - - + Grid.Column="2" + Style="{StaticResource TextExampleCaptionStyle}" + Text="Filled" /> + + + + MaxLines="1" + Text="Normal" + TextTrimming="CharacterEllipsis" + TextWrapping="NoWrap" /> - + + + + + + + + + + + + + + + - - - - + IconColorType="Normal" + IconType="Layered" + Style="{StaticResource IconTest}" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + VerticalAlignment="Center" + Text="ToggleBahvior On + Disabled" /> + + + - - + + Style="{StaticResource IconTest}" + ToggleBehavior="On" /> + Style="{StaticResource IconTest}" + ToggleBehavior="On" /> + Style="{StaticResource IconTest}" + ToggleBehavior="On" /> - - + + IsEnabled="False" + Style="{StaticResource IconTest}" + ToggleBehavior="On" /> + Style="{StaticResource IconTest}" + ToggleBehavior="On" /> + IsEnabled="False" + Style="{StaticResource IconTest}" + ToggleBehavior="On" /> - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + VerticalAlignment="Center" + Text="In ToggleButtons" /> + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + - + + + + + + + - + + + + + - + + + + + - + + + + + + - + + + + + + - + + + + + - HighContrast Manual Toggle - + + + + + - - - - - - + + + + + + - Toggle - + - - - - + Grid.Column="1" + Orientation="Vertical" + Style="{StaticResource StackPanelControlTestingStyle}"> + + + + - Toggle + Disabled - - - - - - - + + + - + - - + + + + + - + + + + + + + + + + - - - - - - - - - - - - - - - + + - + - + + + + + + + + + + + + + + + - + + - + - + - + - + - + - + - + - + - + -