From fa1a2ca7c0a9e6d9f46329f2ddeaebfc5775b763 Mon Sep 17 00:00:00 2001 From: SKProCH Date: Mon, 14 Mar 2022 00:06:17 +0300 Subject: [PATCH 01/92] Fix CurrentTheme ITheme boxing to prevent internal theme modifying from user code --- Material.Styles/Themes/MaterialThemeBase.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Material.Styles/Themes/MaterialThemeBase.cs b/Material.Styles/Themes/MaterialThemeBase.cs index 0def062..0bf328a 100644 --- a/Material.Styles/Themes/MaterialThemeBase.cs +++ b/Material.Styles/Themes/MaterialThemeBase.cs @@ -45,7 +45,7 @@ namespace Material.Styles.Themes { o => o.CurrentTheme, (o, v) => o.CurrentTheme = v); - private ITheme _currentTheme = new ThemeStruct(); + private ThemeStruct _currentTheme = new(); /// /// Get or set current applied theme @@ -54,13 +54,15 @@ namespace Material.Styles.Themes { /// Returns a STRUCT implementing ITheme interface /// public ITheme CurrentTheme { - get => _currentTheme; + get => new ThemeStruct(_currentTheme); set { var oldTheme = _currentTheme; var newTheme = new ThemeStruct(value); - if (SetAndRaise(CurrentThemeProperty, ref _currentTheme, newTheme)) { - StartUpdatingTheme(oldTheme, newTheme); - } + + if (EqualityComparer.Default.Equals(oldTheme, newTheme)) return; + _currentTheme = newTheme; + RaisePropertyChanged(CurrentThemeProperty, oldTheme, newTheme); + StartUpdatingTheme(oldTheme, newTheme); } } From cbfb3ddb743c7fabc73b3b3bec91530f6a229700 Mon Sep 17 00:00:00 2001 From: SKProCH Date: Sat, 19 Mar 2022 14:31:05 +0300 Subject: [PATCH 02/92] Bump DialogHost.Avalonia version to 0.4.0 Fix #163 --- Material.Styles/Material.Styles.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Material.Styles/Material.Styles.csproj b/Material.Styles/Material.Styles.csproj index 42b3145..8685dd2 100644 --- a/Material.Styles/Material.Styles.csproj +++ b/Material.Styles/Material.Styles.csproj @@ -56,7 +56,7 @@ - + From b4e70090a8e20793f2f8dae6446ffc802c8e007e Mon Sep 17 00:00:00 2001 From: SKProCH <29896317+SKProCH@users.noreply.github.com> Date: Sun, 20 Mar 2022 14:39:57 +0300 Subject: [PATCH 03/92] Theme applying fix (#165) * [WIP] Rework theme applying in MaterialTheme * MaterialTheme refactoring --- Material.Styles/Themes/MaterialTheme.cs | 62 +++++++++++++------------ 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/Material.Styles/Themes/MaterialTheme.cs b/Material.Styles/Themes/MaterialTheme.cs index fbdd8ff..12bd243 100644 --- a/Material.Styles/Themes/MaterialTheme.cs +++ b/Material.Styles/Themes/MaterialTheme.cs @@ -1,6 +1,9 @@ using System; +using System.Reactive; +using System.Reactive.Linq; using Avalonia; using Avalonia.Themes.Fluent; +using Avalonia.Threading; using Material.Colors; using Material.Styles.Themes.Base; @@ -11,18 +14,42 @@ namespace Material.Styles.Themes { /// /// You need to setup all these properties: , , /// - public class MaterialTheme : MaterialThemeBase { + public class MaterialTheme : MaterialThemeBase, IDisposable { + private IDisposable _themeUpdaterDisposable = null!; + private ITheme _theme = new ThemeStruct(); + /// /// Initializes a new instance of the class. /// /// The base URL for the XAML context. - public MaterialTheme(Uri baseUri) : base(baseUri) { } + public MaterialTheme(Uri baseUri) : base(baseUri) + => Initialize(); /// /// Initializes a new instance of the class. /// /// The XAML service provider. - public MaterialTheme(IServiceProvider serviceProvider) : base(serviceProvider) { } + public MaterialTheme(IServiceProvider serviceProvider) : base(serviceProvider) + => Initialize(); + + private void Initialize() { + var baseThemeObservable = this.GetObservable(BaseThemeProperty) + .Do(mode => _theme = _theme.SetBaseTheme(mode.GetBaseTheme())) + .Select(_ => Unit.Default); + var primaryColorObservable = this.GetObservable(PrimaryColorProperty) + .Do(color => _theme = _theme.SetPrimaryColor(SwatchHelper.Lookup[(MaterialColor)color])) + .Select(_ => Unit.Default); + var secondaryColorObservable = this.GetObservable(SecondaryColorProperty) + .Do(color => _theme = _theme.SetSecondaryColor(SwatchHelper.Lookup[(MaterialColor)color])) + .Select(_ => Unit.Default); + + _themeUpdaterDisposable = baseThemeObservable + .Merge(primaryColorObservable) + .Merge(secondaryColorObservable) + .Throttle(TimeSpan.FromMilliseconds(100)) + .ObserveOn(new AvaloniaSynchronizationContext()) + .Subscribe(_ => CurrentTheme = _theme); + } public static readonly StyledProperty BaseThemeProperty = AvaloniaProperty.Register(nameof(BaseTheme)); @@ -48,33 +75,8 @@ namespace Material.Styles.Themes { set => SetValue(SecondaryColorProperty, value); } - private bool _isBaseThemePropertyApplied; - private bool _isPrimaryColorPropertyApplied; - private bool _isSecondaryColorPropertyApplied; - private ITheme _theme = new ThemeStruct(); - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { - base.OnPropertyChanged(change); - if (change.Property == BaseThemeProperty) { - _theme = _theme.SetBaseTheme(BaseTheme.GetBaseTheme()); - _isBaseThemePropertyApplied = true; - TryApplyTheme(); - } - if (change.Property == PrimaryColorProperty) { - _theme = _theme.SetPrimaryColor(SwatchHelper.Lookup[(MaterialColor)PrimaryColor]); - _isPrimaryColorPropertyApplied = true; - TryApplyTheme(); - } - if (change.Property == SecondaryColorProperty) { - _theme = _theme.SetSecondaryColor(SwatchHelper.Lookup[(MaterialColor)SecondaryColor]); - _isSecondaryColorPropertyApplied = true; - TryApplyTheme(); - } - - void TryApplyTheme() { - if (_isBaseThemePropertyApplied && _isPrimaryColorPropertyApplied && _isSecondaryColorPropertyApplied) { - this.CurrentTheme = _theme; - } - } + public void Dispose() { + _themeUpdaterDisposable.Dispose(); } } } \ No newline at end of file From e5875e2a87b421727f471b6fa19fe3defdba035d Mon Sep 17 00:00:00 2001 From: CreateLab <33497292+CreateLab@users.noreply.github.com> Date: Sun, 20 Mar 2022 18:54:02 +0300 Subject: [PATCH 04/92] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index df67d57..369aae4 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Check out the [getting started](https://github.com/AvaloniaCommunity/Material.Av 2. Edit `App.xaml` file: +```xml @@ -36,7 +37,7 @@ Check out the [getting started](https://github.com/AvaloniaCommunity/Material.Av - +``` - [Advanced theming](https://github.com/AvaloniaCommunity/Material.Avalonia/wiki/Advanced-Theming) wiki page - [Nightly packages](https://github.com/AvaloniaCommunity/Material.Avalonia/wiki/Using-nightly-build-feed) wiki page - [Material Design Icons](https://github.com/AvaloniaUtils/Material.Icons.Avalonia) icon pack support From e9472b1244f6e5601e64c17a7bfe979ed19afa3f Mon Sep 17 00:00:00 2001 From: SKProCH Date: Tue, 22 Mar 2022 21:23:29 +0300 Subject: [PATCH 05/92] Another README.md update --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 369aae4..0d5bea8 100644 --- a/README.md +++ b/README.md @@ -29,15 +29,15 @@ Check out the [getting started](https://github.com/AvaloniaCommunity/Material.Av 2. Edit `App.xaml` file: -```xml - - - - - -``` + ```xaml + + + + + + ``` - [Advanced theming](https://github.com/AvaloniaCommunity/Material.Avalonia/wiki/Advanced-Theming) wiki page - [Nightly packages](https://github.com/AvaloniaCommunity/Material.Avalonia/wiki/Using-nightly-build-feed) wiki page - [Material Design Icons](https://github.com/AvaloniaUtils/Material.Icons.Avalonia) icon pack support From e6f642d274fd497da19c535f3cdc517c2346405d Mon Sep 17 00:00:00 2001 From: appleneko2001 Date: Sun, 17 Apr 2022 20:02:40 +0900 Subject: [PATCH 06/92] Use CornerRadius from TemplatedControl instead --- Material.Demo/Pages/ButtonsDemo.axaml | 4 ++-- Material.Demo/Pages/IconsDemo.axaml | 4 ++-- Material.Styles/Button.xaml | 9 +++++---- Material.Styles/ButtonSpinner.xaml | 8 ++++---- Material.Styles/RepeatButton.xaml | 6 +++--- Material.Styles/ToggleButton.xaml | 8 ++++---- 6 files changed, 20 insertions(+), 19 deletions(-) diff --git a/Material.Demo/Pages/ButtonsDemo.axaml b/Material.Demo/Pages/ButtonsDemo.axaml index afd30f5..c8d9ba4 100644 --- a/Material.Demo/Pages/ButtonsDemo.axaml +++ b/Material.Demo/Pages/ButtonsDemo.axaml @@ -27,7 +27,7 @@ @@ -108,7 +108,7 @@ diff --git a/Material.Styles/Button.xaml b/Material.Styles/Button.xaml index e4efed3..5e9e228 100644 --- a/Material.Styles/Button.xaml +++ b/Material.Styles/Button.xaml @@ -11,6 +11,7 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + - - - - + + + + + + + + + + + - + + + + + + + + + + \ No newline at end of file From a04d9897e13c0f54cb78028ec7742392df42a23c Mon Sep 17 00:00:00 2001 From: appleneko2001 Date: Sun, 17 Apr 2022 22:21:32 +0900 Subject: [PATCH 12/92] Change transition of thumb switcher --- Material.Styles/ToggleSwitch.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Material.Styles/ToggleSwitch.xaml b/Material.Styles/ToggleSwitch.xaml index 638ed8f..1ddfd6b 100644 --- a/Material.Styles/ToggleSwitch.xaml +++ b/Material.Styles/ToggleSwitch.xaml @@ -103,7 +103,7 @@ From 633a075c5d8377b31cbe02c96d7ee3436f4653d0 Mon Sep 17 00:00:00 2001 From: appleneko2001 Date: Sun, 17 Apr 2022 22:28:08 +0900 Subject: [PATCH 13/92] Update CheckBox style --- Material.Styles/CheckBox.xaml | 123 ++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 51 deletions(-) diff --git a/Material.Styles/CheckBox.xaml b/Material.Styles/CheckBox.xaml index 04e4c2a..e57fa2c 100644 --- a/Material.Styles/CheckBox.xaml +++ b/Material.Styles/CheckBox.xaml @@ -1,6 +1,5 @@ @@ -8,71 +7,83 @@ 0.26 - - - + + + + + + + + + + + + + + + + + + + + + + + + - + - \ No newline at end of file From 5dcd5a81938b6f361d83a133e3421d4e0c90330d Mon Sep 17 00:00:00 2001 From: appleneko2001 Date: Sun, 17 Apr 2022 23:41:59 +0900 Subject: [PATCH 14/92] Use CornerRadius, remove nonsense property binding (ListBoxItem) --- Material.Styles/ListBoxItem.xaml | 134 ++++++++++++++++--------------- 1 file changed, 69 insertions(+), 65 deletions(-) diff --git a/Material.Styles/ListBoxItem.xaml b/Material.Styles/ListBoxItem.xaml index 0691e7a..c37d5d8 100644 --- a/Material.Styles/ListBoxItem.xaml +++ b/Material.Styles/ListBoxItem.xaml @@ -1,72 +1,76 @@ - - - - + + + - - - - - - + + + + + + + + \ No newline at end of file From 093754f36d2f3e8b962e8b6c5ff78eff6b3518cf Mon Sep 17 00:00:00 2001 From: appleneko2001 Date: Wed, 20 Apr 2022 22:25:06 +0900 Subject: [PATCH 15/92] Update ToggleSwitch style (Fix behaviours and bindings, add transitions to thumb and track) --- Material.Styles/ToggleSwitch.xaml | 74 ++++++++++++++++++------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/Material.Styles/ToggleSwitch.xaml b/Material.Styles/ToggleSwitch.xaml index 1ddfd6b..254ed0a 100644 --- a/Material.Styles/ToggleSwitch.xaml +++ b/Material.Styles/ToggleSwitch.xaml @@ -12,15 +12,15 @@ - + - + - - - + @@ -52,36 +52,37 @@ HorizontalAlignment="Left" VerticalAlignment="Center" Width="20" Height="20"> - + Width="20" Height="20" + HorizontalAlignment="Center" + VerticalAlignment="Center" + assists:ShadowAssist.ShadowDepth="{TemplateBinding (assists:ShadowAssist.ShadowDepth)}" > + + + + + - - + + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/> @@ -99,16 +100,23 @@ - - - + @@ -151,15 +159,13 @@ - - - @@ -170,6 +176,12 @@ + + + + - diff --git a/Material.Styles/Material.Styles.csproj b/Material.Styles/Material.Styles.csproj index 8685dd2..c39f525 100644 --- a/Material.Styles/Material.Styles.csproj +++ b/Material.Styles/Material.Styles.csproj @@ -56,7 +56,6 @@ - diff --git a/Material.Styles/MaterialToolKit.xaml b/Material.Styles/MaterialToolKit.xaml index 6b082b7..d4d641f 100644 --- a/Material.Styles/MaterialToolKit.xaml +++ b/Material.Styles/MaterialToolKit.xaml @@ -20,11 +20,6 @@ - - - - - From 88978ca7403f7020acb2b7e6deda10a070d93654 Mon Sep 17 00:00:00 2001 From: SKProCH Date: Mon, 9 May 2022 19:29:22 +0300 Subject: [PATCH 18/92] Bump DialogHost.Avalonia to 0.6.0-rc0 --- Material.Demo/Material.Demo.csproj | 10 +++++----- Material.Demo/Pages/DialogDemo.axaml | 2 +- Material.Demo/Pages/DialogDemo.axaml.cs | 5 +++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Material.Demo/Material.Demo.csproj b/Material.Demo/Material.Demo.csproj index 69d6e5f..1af3e2c 100644 --- a/Material.Demo/Material.Demo.csproj +++ b/Material.Demo/Material.Demo.csproj @@ -6,13 +6,13 @@ enable - - - - + + + + - + diff --git a/Material.Demo/Pages/DialogDemo.axaml b/Material.Demo/Pages/DialogDemo.axaml index 3758bd6..0150bed 100644 --- a/Material.Demo/Pages/DialogDemo.axaml +++ b/Material.Demo/Pages/DialogDemo.axaml @@ -3,7 +3,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:styles="clr-namespace:Material.Styles;assembly=Material.Styles" xmlns:viewModels="clr-namespace:Material.Demo.ViewModels" - xmlns:dialogHost="clr-namespace:DialogHost;assembly=DialogHost.Avalonia" + xmlns:dialogHost="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia" xmlns:models="clr-namespace:Material.Demo.Models" x:Class="Material.Demo.Pages.DialogDemo" x:DataType="viewModels:DialogDemoViewModel"> diff --git a/Material.Demo/Pages/DialogDemo.axaml.cs b/Material.Demo/Pages/DialogDemo.axaml.cs index 3f39dd8..f4e1a56 100644 --- a/Material.Demo/Pages/DialogDemo.axaml.cs +++ b/Material.Demo/Pages/DialogDemo.axaml.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; +using DialogHostAvalonia; using Material.Demo.Models; using Material.Demo.ViewModels; @@ -23,12 +24,12 @@ namespace Material.Demo.Pages } private void OpenDialogWithView(object? sender, RoutedEventArgs e) { - DialogHost.DialogHost.Show(this.Resources["Sample2View"]!, "MainDialogHost"); + DialogHost.Show(this.Resources["Sample2View"]!, "MainDialogHost"); } private void OpenDialogWithModel(object? sender, RoutedEventArgs e) { // View that associated with this model defined at DialogContentTemplate in DialogDemo.axaml - DialogHost.DialogHost.Show(new Sample2Model(new Random().Next(0, 100)), "MainDialogHost"); + DialogHost.Show(new Sample2Model(new Random().Next(0, 100)), "MainDialogHost"); } private void OpenMoreDialogHostExamples(object? sender, RoutedEventArgs e) { From a8da69c873dd69e7ed63d104c1549459c4365a96 Mon Sep 17 00:00:00 2001 From: appleneko2001 Date: Fri, 3 Jun 2022 04:49:04 +0900 Subject: [PATCH 19/92] Prepare a static part naming collections --- Material.Styles/Resources/Naming/PartNames.cs | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 Material.Styles/Resources/Naming/PartNames.cs diff --git a/Material.Styles/Resources/Naming/PartNames.cs b/Material.Styles/Resources/Naming/PartNames.cs new file mode 100644 index 0000000..7fcf112 --- /dev/null +++ b/Material.Styles/Resources/Naming/PartNames.cs @@ -0,0 +1,134 @@ +namespace Material.Styles.Resources.Naming +{ + /// + /// Constant naming collections of templated elements in control. + /// + public static class PartNames + { + #region Common use, including effects + + public static string PartRootBorder => "PART_RootBorder"; + + public static string PartRootPanel => "PART_RootPanel"; + + public static string PartContentPresenter => "PART_ContentPresenter"; + + public static string PartIconPresenter => "PART_IconPresenter"; + + public static string PartHeaderPresenter => "PART_HeaderPresenter"; + + public static string PartItemsPresenter => "PART_ItemsPresenter"; + + public static string PartInnerPanel => "PART_InnerPanel"; + + public static string PartContentPanel => "PART_ContentPanel"; + + public static string PartInnerBorder => "PART_InnerBorder"; + + public static string PartHoverEffect => "PART_HoverEffect"; + + public static string PartScrollViewer => "PART_ScrollViewer"; + + public static string PartRipple => "PART_Ripple"; + + #endregion + + #region For TextBox, ComboBoxes or etc. + + public static string PartPlaceholderText => "PART_PlaceholderText"; + + public static string PartTextPresenter => "PART_TextPresenter"; + + public static string PartDataValidation => "PART_DataValidation"; + + public static string PartHintsText => "PART_HintsText"; + + public static string PartUnderline => "PART_Underline"; + + public static string PartLabelText => "PART_LabelText"; + + public static string PartLabelRootBorder => "PART_LabelRootBorder"; + + #endregion + + #region For Slider + + /// + ///

This name is used for AvaloniaUI integration (Required).

+ /// Use this name on track of slider. + /// You should do that otherwise your application will crash after click slider. + ///
+ public static string PartTrack => "PART_Track"; + + public static string PartTrackBorderBar => "PART_TrackBorderBar"; + + public static string PartSliderThumb => "PART_SliderThumb"; + + /// + ///

This name is used for AvaloniaUI integration.

+ /// Use this name on RepeatButton of track of slider to get a clickable decrease value track. + ///
+ public static string PartDecreaseButton => "PART_DecreaseButton"; + + /// + ///

This name is used for AvaloniaUI integration.

+ /// Use this name on RepeatButton of track of slider to get a clickable increase value track. + ///
+ public static string PartIncreaseButton => "PART_IncreaseButton"; + + public static string PartThumbGrip => "PART_ThumbGrip"; + + #endregion + + #region For ToggleSwitch + + public static string PartSwitchBorder => "PART_SwitchBorder"; + + public static string PartSwitchKnobBorder => "PART_SwitchKnobBorder"; + + public static string PartKnobOnContentPresenter => "PART_KnobOnContentPresenter"; + + public static string PartKnobOffContentPresenter => "PART_KnobOffContentPresenter"; + + /// + ///

This name is used for AvaloniaUI integration.

+ /// Use this name on canvas of ToggleSwitch to get a zone for draggable knob + ///
+ public static string AvaloniaSwitchKnob => "SwitchKnob"; + + /// + ///

This name is used for AvaloniaUI integration.

+ /// Use this name on panel under of canvas of ToggleSwitch to get a draggable knob + ///
+ public static string AvaloniaMovingKnobs => "MovingKnobs"; + + #endregion + + #region For ProgressBar + + /// + ///

This name is used for AvaloniaUI integration (Required).

+ /// Use this name on border to make ProgressBar recognize that border as active indicator. + /// Otherwise ProgressBar will unable to work correctly. + ///
+ public static string AvaloniaProgressBarIndicator => "PART_Indicator"; + + #endregion + + public static string PartInputGestureText => "PART_InputGestureText"; + + public static string PartVisualLayer => "VisualLayer"; + + /// + /// General use. + /// Please use this name on templated border of controls that have behaviours (Selected / Hovered / Clicked behaviour or etc.) + /// + public static string PartBehaviourEffect => "PART_BehaviourEffect"; + + public static string PartExpanderButton => "PART_ExpanderButton"; + + public static string PartPopup => "PART_Popup"; + + public static string PartCard => "PART_Card"; + } +} \ No newline at end of file From 83428024fd9ac2628475dfba4154b283f9fce5b2 Mon Sep 17 00:00:00 2001 From: appleneko2001 Date: Fri, 3 Jun 2022 04:53:35 +0900 Subject: [PATCH 20/92] Update converters (optimize and improve rect clipping, add maximize corner radius, discrete slider popup positioning improvements and etc.) --- .../AutoCorrectPositionConverter.cs | 25 ++-- .../MarginValueMultiplyConverter.cs | 18 ++- .../MaximizeCornerRadiusConverter.cs | 28 +++++ .../Parameters/RectHollowClipParameter.cs | 10 ++ .../Converters/RangeToSweepConverter.cs | 12 +- .../Converters/RectHollowClipConverter.cs | 107 ++++++++++++++++++ .../RectangleHollowGeometryConverter.cs | 96 ---------------- 7 files changed, 179 insertions(+), 117 deletions(-) create mode 100644 Material.Styles/Converters/MaximizeCornerRadiusConverter.cs create mode 100644 Material.Styles/Converters/Parameters/RectHollowClipParameter.cs create mode 100644 Material.Styles/Converters/RectHollowClipConverter.cs delete mode 100644 Material.Styles/Converters/RectangleHollowGeometryConverter.cs diff --git a/Material.Styles/Converters/AutoCorrectPositionConverter.cs b/Material.Styles/Converters/AutoCorrectPositionConverter.cs index bab5078..48d5b40 100644 --- a/Material.Styles/Converters/AutoCorrectPositionConverter.cs +++ b/Material.Styles/Converters/AutoCorrectPositionConverter.cs @@ -2,10 +2,8 @@ using System.Collections.Generic; using System.Globalization; using Avalonia; -using Avalonia.Controls; using Avalonia.Data.Converters; using Avalonia.Media; -using Avalonia.Media.Transformation; using Avalonia.VisualTree; namespace Material.Styles.Converters @@ -16,28 +14,27 @@ namespace Material.Styles.Converters private static double GetOffLeft(Rect bounds, double offsetX) => offsetX; - private static double GetOffRight(Rect bounds, double windowW, double offsetX) => offsetX + (bounds.Width) - windowW; + private static double GetOffRight(Rect bounds, double windowW, double offsetX) => offsetX + bounds.Width - windowW; - private static Vector GetTranslate(TransformedBounds bounds) + private static Vector GetTranslate(Matrix m) { - return new Vector(bounds.Transform.M31, bounds.Transform.M32); + return Matrix.TryDecomposeTransform(m, out var decomposed) ? + decomposed.Translate : Vector.Zero; } - private Vector _prevCorrect = Vector.One; - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - var transformedBounds = value as TransformedBounds?; - double offsetX = 0; - if (transformedBounds.HasValue) + if(value is TransformedBounds postTransformations) { - var bounds = transformedBounds.Value; + var t = postTransformations.Transform; + var b = postTransformations.Bounds; + var c = postTransformations.Clip; - var translate = GetTranslate(bounds); + var translate = GetTranslate(t); - var left = GetOffLeft(bounds.Bounds, translate.X - _prevCorrect.X); - var right = GetOffRight(bounds.Bounds, bounds.Clip.Width, translate.X- _prevCorrect.X); + var left = GetOffLeft(b, translate.X); + var right = GetOffRight(b, c.Width, translate.X); if (left < 0) { diff --git a/Material.Styles/Converters/MarginValueMultiplyConverter.cs b/Material.Styles/Converters/MarginValueMultiplyConverter.cs index 0e9f0c6..d29ded1 100644 --- a/Material.Styles/Converters/MarginValueMultiplyConverter.cs +++ b/Material.Styles/Converters/MarginValueMultiplyConverter.cs @@ -20,11 +20,21 @@ namespace Material.Styles.Converters _ => MarginMultiplyParameter.Default }; - if (value is double v && !double.IsNaN(v)) - return new Thickness(v * param.LeftMultiplier, v * param.TopMultiplier, v * param.RightMultiplier, - v * param.BottomMultiplier); + var result = value switch + { + // If value is double primitive type + double v when !double.IsNaN(v) => new Thickness(v * param.LeftMultiplier, v * param.TopMultiplier, + v * param.RightMultiplier, v * param.BottomMultiplier), + + // or value is 32-bit integer primitive type + int i => new Thickness(i * param.LeftMultiplier, i * param.TopMultiplier, i * param.RightMultiplier, + i * param.BottomMultiplier), + + // or its unsupported type + _ => Thickness.Parse("0") + }; - return Thickness.Parse("0"); + return result; } public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) diff --git a/Material.Styles/Converters/MaximizeCornerRadiusConverter.cs b/Material.Styles/Converters/MaximizeCornerRadiusConverter.cs new file mode 100644 index 0000000..d63f569 --- /dev/null +++ b/Material.Styles/Converters/MaximizeCornerRadiusConverter.cs @@ -0,0 +1,28 @@ +using System; +using System.Globalization; +using Avalonia; +using Avalonia.Data.Converters; + +namespace Material.Styles.Converters +{ + public class MaximizeCornerRadiusConverter : IValueConverter + { + public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if(value is Rect r) + return new CornerRadius(r.Left/2, + r.Top/2, + r.Right/2, + r.Bottom/2); + + // Works only for Skia backend rendering + // DX2D or other backends might works abnormally + return new CornerRadius(double.MaxValue); + } + + public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Material.Styles/Converters/Parameters/RectHollowClipParameter.cs b/Material.Styles/Converters/Parameters/RectHollowClipParameter.cs new file mode 100644 index 0000000..2ef6f2d --- /dev/null +++ b/Material.Styles/Converters/Parameters/RectHollowClipParameter.cs @@ -0,0 +1,10 @@ +using Avalonia; + +namespace Material.Styles.Converters.Parameters +{ + public class RectHollowClipParameter + { + public Thickness Margin { get; set; } + public Point Offset { get; set; } + } +} \ No newline at end of file diff --git a/Material.Styles/Converters/RangeToSweepConverter.cs b/Material.Styles/Converters/RangeToSweepConverter.cs index 9444874..88b109c 100644 --- a/Material.Styles/Converters/RangeToSweepConverter.cs +++ b/Material.Styles/Converters/RangeToSweepConverter.cs @@ -9,17 +9,23 @@ namespace Material.Styles.Converters { public class RangeToSweepConverter : IMultiValueConverter { - public object Convert(IList values, Type targetType, object? parameter, CultureInfo culture) + public object? Convert(IList values, Type targetType, object? parameter, CultureInfo culture) { double min = 0, max = 100, val = 0; + + for (var i = 0; i < values.Count; i++) + { + if (values[i] == null) + return null; + } if (values[0] is double value) val = value; - if (values.Count >= 2 && values[1] is double minimum) + if (values[1] is double minimum) min = minimum; - if (values.Count >= 3 && values[2] is double maximum) + if (values[2] is double maximum) max = maximum; var m = max - min; diff --git a/Material.Styles/Converters/RectHollowClipConverter.cs b/Material.Styles/Converters/RectHollowClipConverter.cs new file mode 100644 index 0000000..618c037 --- /dev/null +++ b/Material.Styles/Converters/RectHollowClipConverter.cs @@ -0,0 +1,107 @@ +using Avalonia; +using Avalonia.Data.Converters; +using System; +using System.Collections.Generic; +using System.Globalization; +using Avalonia.Media; +using Avalonia.Media.Transformation; +using Material.Styles.Converters.Parameters; + +namespace Material.Styles.Converters +{ + /// + /// Converter for creating rectangle geometry with hollow. Used for Outline TextBox. + /// + public class RectHollowClipConverter : IMultiValueConverter + { + public object Convert(IList values, Type targetType, object? parameter, CultureInfo culture) + { + double hOffset = 4, vOffset = -8; + var t = new Thickness(2); + + if (parameter is RectHollowClipParameter param) + { + t = param.Margin; + hOffset = param.Offset.X; + vOffset = param.Offset.Y; + } + + var main = Rect.Empty; + var hollow = Rect.Empty; + + Geometry result; + + try + { + var s = new Point(1, 1); + + if (values[0] is Rect outer && values[1] is Rect inner) + { + main = outer; + hollow = inner; + } + if (values.Count == 3 && values[2] is TransformOperations transform) + { + if (Matrix.TryDecomposeTransform(transform.Value, out var d)) + s = new Point(d.Scale.X, d.Scale.Y); + } + + // Base zone + var m0 = main.TopLeft; + var m1 = main.BottomRight; + + // Hollow zone + var h0 = Multiply(hollow.TopLeft - new Point(t.Left - hOffset, t.Top - vOffset), s); + var h1 = Multiply(hollow.BottomRight + new Point(t.Right + hOffset, t.Bottom + vOffset), s); + + // Create geometry + var outerGeometry = new StreamGeometry(); + var innerGeometry = new StreamGeometry(); + + using (var ctx = outerGeometry.Open()) + { + ctx.BeginFigure(m0, true); + ctx.LineTo(new Point(m1.X, m0.Y)); + ctx.LineTo(new Point(m1.X, m1.Y)); + ctx.LineTo(new Point(m0.X, m1.Y)); + ctx.EndFigure(true); + } + + using (var ctx = innerGeometry.Open()) + { + ctx.BeginFigure(new Point(h0.X, h0.Y), true); + ctx.LineTo(new Point(h1.X, h0.Y)); + ctx.LineTo(new Point(h1.X, h1.Y)); + ctx.LineTo(new Point(h0.X, h1.Y)); + ctx.EndFigure(true); + } + + result = new CombinedGeometry(GeometryCombineMode.Xor, innerGeometry, outerGeometry); + } + catch + { + var m0 = main.TopLeft; + var m1 = main.BottomRight; + + var outerGeometry = new StreamGeometry(); + + using var ctx = outerGeometry.Open(); + + ctx.BeginFigure(m0, true); + ctx.LineTo(new Point(m1.X, m0.Y)); + ctx.LineTo(new Point(m1.X, m1.Y)); + ctx.LineTo(new Point(m0.X, m1.Y)); + ctx.EndFigure(true); + + result = outerGeometry; + } + + return result; + } + + private static Point Multiply(Point p, Point s) + { + return new Point(p.X * s.X, p.Y * s.Y); + } + } +} \ No newline at end of file diff --git a/Material.Styles/Converters/RectangleHollowGeometryConverter.cs b/Material.Styles/Converters/RectangleHollowGeometryConverter.cs deleted file mode 100644 index 52d1193..0000000 --- a/Material.Styles/Converters/RectangleHollowGeometryConverter.cs +++ /dev/null @@ -1,96 +0,0 @@ -using Avalonia; -using Avalonia.Data.Converters; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Text; -using Avalonia.Controls; -using Avalonia.Media; -using Avalonia.VisualTree; - -namespace Material.Styles.Converters -{ - /// - /// Converter for creating rectangle geometry with hollow. Basically used for Outline TextBox. - /// - public class RectangleHollowGeometryConverter : IMultiValueConverter - { - public object Convert(IList values, Type targetType, object parameter, CultureInfo culture) - { - double offsetL = 4, offsetR = 4; - Thickness t = new Thickness(4, 0); - - if (parameter != null) - { - try - { - t = Thickness.Parse(parameter.ToString()); - } - catch - { - // ignored - } - } - - Rect main = Rect.Empty; - Rect hollow = Rect.Empty; - - StreamGeometry result = null; - - try - { - Point s = new Point(1, 1); - - if ((values[0] is Rect mainR)) - { - main = mainR; - } - if ((values[1] is Rect hollowR)) - { - hollow = hollowR; - } - if ((values[2] is TransformedBounds tb)) - { - s = new Point(tb.Transform.M11, tb.Transform.M22); - } - - // Base zone - var m0 = main.TopLeft; - var m1 = main.BottomRight; - - // Hollow zone - var h0 = hollow.TopLeft + new Point(offsetL - t.Left, 0); - var h1 = hollow.BottomRight + new Point(offsetR + t.Right, 0); - - // Limiter - var lL = main.Left - t.Left + offsetL; - var lR = main.Right + t.Right + offsetR; - - var str = $"M {m0.X} {m0.Y} " + - $"L {m1.X} {m0.Y} " + - $"L {m1.X} {m1.Y} " + - $"L {m0.X} {m1.Y} z " + - $"M {Math.Max(h0.X * s.X, lL)} {h0.Y * s.Y} " + - $"L {Math.Min(h1.X * s.X, lR)} {h0.Y * s.Y} " + - $"L {Math.Min(h1.X * s.X, lR)} {h1.Y * s.Y} " + - $"L {Math.Max(h0.X * s.X, lL)} {h1.Y * s.Y} z "; - - str = str.Replace(",", "."); - - result = StreamGeometry.Parse(str); - } - catch(Exception e) - { - var m0 = main.TopLeft; - var m1 = main.BottomRight; - - result = StreamGeometry.Parse($"M {m0.X} {m0.Y} " + - $"L {m1.X} {m0.Y} " + - $"L {m1.X} {m1.Y} " + - $"L {m0.X} {m1.Y} z "); - } - - return result; - } - } -} \ No newline at end of file From aa7023e6d770fd179620d8f78b8ea51f21e866df Mon Sep 17 00:00:00 2001 From: appleneko2001 Date: Fri, 3 Jun 2022 04:54:15 +0900 Subject: [PATCH 21/92] Add IsAllowedRaiseRipple property --- Material.Ripple/RippleEffect.cs | 35 ++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/Material.Ripple/RippleEffect.cs b/Material.Ripple/RippleEffect.cs index 49b7191..08f5c60 100644 --- a/Material.Ripple/RippleEffect.cs +++ b/Material.Ripple/RippleEffect.cs @@ -24,17 +24,20 @@ namespace Material.Ripple { private void PointerPressedHandler(object sender, PointerPressedEventArgs e) { - if (_pointers == 0) - { - // Only first pointer can arrive a ripple - _pointers++; - var r = CreateRipple(e, RaiseRippleCenter); - _last = r; + if(!IsAllowedRaiseRipple) + return; - // Attach ripple instance to canvas - PART_RippleCanvasRoot.Children.Add(r); - r.RunFirstStep(); - } + if (_pointers != 0) + return; + + // Only first pointer can arrive a ripple + _pointers++; + var r = CreateRipple(e, RaiseRippleCenter); + _last = r; + + // Attach ripple instance to canvas + PART_RippleCanvasRoot.Children.Add(r); + r.RunFirstStep(); } private void PointerReleasedHandler(object sender, PointerReleasedEventArgs e) @@ -103,7 +106,7 @@ namespace Material.Ripple { #region Styled properties public static readonly StyledProperty RippleFillProperty = - AvaloniaProperty.Register(nameof(RippleFill), SolidColorBrush.Parse("#FFF")); + AvaloniaProperty.Register(nameof(RippleFill), inherits: true); public IBrush RippleFill { get => GetValue(RippleFillProperty); @@ -111,7 +114,7 @@ namespace Material.Ripple { } public static readonly StyledProperty RippleOpacityProperty = - AvaloniaProperty.Register(nameof(RippleOpacity), 0.6); + AvaloniaProperty.Register(nameof(RippleOpacity), inherits: true); public double RippleOpacity { get => GetValue(RippleOpacityProperty); @@ -125,6 +128,14 @@ namespace Material.Ripple { get => GetValue(RaiseRippleCenterProperty); set => SetValue(RaiseRippleCenterProperty, value); } + + public static readonly StyledProperty IsAllowedRaiseRippleProperty = + AvaloniaProperty.Register(nameof(IsAllowedRaiseRipple)); + + public bool IsAllowedRaiseRipple { + get => GetValue(IsAllowedRaiseRippleProperty); + set => SetValue(IsAllowedRaiseRippleProperty, value); + } #endregion Styled properties } From 9822f2eaf99e5bc8682a06abf557add56f029342 Mon Sep 17 00:00:00 2001 From: appleneko2001 Date: Fri, 3 Jun 2022 04:55:13 +0900 Subject: [PATCH 22/92] Migrate default value to style setters, use newer no-transitions --- Material.Ripple/RippleEffect.xaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Material.Ripple/RippleEffect.xaml b/Material.Ripple/RippleEffect.xaml index edbc57e..83202ea 100644 --- a/Material.Ripple/RippleEffect.xaml +++ b/Material.Ripple/RippleEffect.xaml @@ -2,6 +2,9 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:cc="clr-namespace:Material.Ripple"> \ No newline at end of file diff --git a/Material.Dialog/Styles/EmbeddedDialogWindow.axaml b/Material.Dialog/Styles/EmbeddedDialogWindow.axaml new file mode 100644 index 0000000..2e7e0c3 --- /dev/null +++ b/Material.Dialog/Styles/EmbeddedDialogWindow.axaml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Material.Dialog/Styles/StyleInclude.axaml b/Material.Dialog/Styles/StyleInclude.axaml index 1323161..b2f5a7e 100644 --- a/Material.Dialog/Styles/StyleInclude.axaml +++ b/Material.Dialog/Styles/StyleInclude.axaml @@ -1,6 +1,7 @@  + xmlns:assists="clr-namespace:Material.Styles.Assists;assembly=Material.Styles" + xmlns:controls="clr-namespace:Material.Dialog.Controls" > - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Material.Dialog/ViewModels/AlertDialogViewModel.cs b/Material.Dialog/ViewModels/AlertDialogViewModel.cs index 898ded6..94e7751 100644 --- a/Material.Dialog/ViewModels/AlertDialogViewModel.cs +++ b/Material.Dialog/ViewModels/AlertDialogViewModel.cs @@ -4,9 +4,9 @@ namespace Material.Dialog.ViewModels { public class AlertDialogViewModel : DialogWindowViewModel { - public AlertDialogViewModel(AlertDialog dialog) + public AlertDialogViewModel(AlertDialog dialog) : base(dialog) { - _window = dialog; + } } } diff --git a/Material.Dialog/ViewModels/CustomDialogViewModel.cs b/Material.Dialog/ViewModels/CustomDialogViewModel.cs index ab256f0..53a7cc8 100644 --- a/Material.Dialog/ViewModels/CustomDialogViewModel.cs +++ b/Material.Dialog/ViewModels/CustomDialogViewModel.cs @@ -27,9 +27,9 @@ namespace Material.Dialog.ViewModels } } - public CustomDialogViewModel(CustomDialog dialog) + public CustomDialogViewModel(CustomDialog dialog) : base(dialog) { - _window = dialog; + } } } \ No newline at end of file diff --git a/Material.Dialog/ViewModels/DatePickerDialogViewModel.cs b/Material.Dialog/ViewModels/DatePickerDialogViewModel.cs index 89b012a..10fdc14 100644 --- a/Material.Dialog/ViewModels/DatePickerDialogViewModel.cs +++ b/Material.Dialog/ViewModels/DatePickerDialogViewModel.cs @@ -24,9 +24,8 @@ namespace Material.Dialog.ViewModels } } - public DatePickerDialogViewModel(DatePickerDialog dialog) + public DatePickerDialogViewModel(DatePickerDialog dialog) : base(dialog) { - _window = dialog; ButtonClick = new MaterialDialogRelayCommand(OnPressButton, CanPressButton); } diff --git a/Material.Dialog/ViewModels/DialogWindowViewModel.cs b/Material.Dialog/ViewModels/DialogWindowViewModel.cs index 1428444..b19a299 100644 --- a/Material.Dialog/ViewModels/DialogWindowViewModel.cs +++ b/Material.Dialog/ViewModels/DialogWindowViewModel.cs @@ -9,7 +9,18 @@ namespace Material.Dialog.ViewModels { public abstract class DialogWindowViewModel : DialogViewModelBase { - protected Window _window; + public DialogWindowViewModel(Window window) + { + Window = window; + } + + private Window _window; + + protected Window Window + { + get => _window; + private set => _window = value; + } #region Base Properties diff --git a/Material.Dialog/ViewModels/Elements/DialogButtonViewModel.cs b/Material.Dialog/ViewModels/Elements/DialogButtonViewModel.cs index 713f2a2..49a9ff9 100644 --- a/Material.Dialog/ViewModels/Elements/DialogButtonViewModel.cs +++ b/Material.Dialog/ViewModels/Elements/DialogButtonViewModel.cs @@ -46,7 +46,7 @@ namespace Material.Dialog.ViewModels.Elements } } - internal ICommand _command; + private ICommand _command; public ICommand Command { diff --git a/Material.Dialog/ViewModels/Elements/ObsoleteDialogButtonViewModel.cs b/Material.Dialog/ViewModels/Elements/ObsoleteDialogButtonViewModel.cs index 4d7ec46..71e2f37 100644 --- a/Material.Dialog/ViewModels/Elements/ObsoleteDialogButtonViewModel.cs +++ b/Material.Dialog/ViewModels/Elements/ObsoleteDialogButtonViewModel.cs @@ -7,7 +7,7 @@ namespace Material.Dialog.ViewModels.Elements public ObsoleteDialogButtonViewModel(DialogWindowViewModel parent, object content, string result) : base(parent, content) { _result = result; - _command = new MaterialDialogRelayCommand(OnExecuteCommandHandler, CanExecuteCommandHandler); + Command = new MaterialDialogRelayCommand(OnExecuteCommandHandler, CanExecuteCommandHandler); } private bool CanExecuteCommandHandler(object arg) diff --git a/Material.Dialog/ViewModels/TextFieldDialogViewModel.cs b/Material.Dialog/ViewModels/TextFieldDialogViewModel.cs index f09bc2a..60bfc93 100644 --- a/Material.Dialog/ViewModels/TextFieldDialogViewModel.cs +++ b/Material.Dialog/ViewModels/TextFieldDialogViewModel.cs @@ -38,9 +38,8 @@ namespace Material.Dialog.ViewModels internal set => _negativeButton = value; }*/ - public TextFieldDialogViewModel(TextFieldDialog dialog) + public TextFieldDialogViewModel(TextFieldDialog dialog) : base(dialog) { - _window = dialog; SubmitCommand = new MaterialDialogRelayCommand(OnPressButton, CanPressButton); } @@ -109,7 +108,7 @@ namespace Material.Dialog.ViewModels result.fieldsResult = fields.ToArray(); button.Parent.DialogResult = result; - _window.Close(); + Window.Close(); UnbindValidateHandler(); }); } diff --git a/Material.Dialog/ViewModels/TimePickerDialogViewModel.cs b/Material.Dialog/ViewModels/TimePickerDialogViewModel.cs index 9780d6b..fee9444 100644 --- a/Material.Dialog/ViewModels/TimePickerDialogViewModel.cs +++ b/Material.Dialog/ViewModels/TimePickerDialogViewModel.cs @@ -129,9 +129,8 @@ namespace Material.Dialog.ViewModels } } - public TimePickerDialogViewModel(TimePickerDialog dialog) + public TimePickerDialogViewModel(TimePickerDialog dialog) : base(dialog) { - _window = dialog; ButtonClick = new MaterialDialogRelayCommand(OnPressButton, CanPressButton); } diff --git a/Material.Dialog/Views/AlertDialog.axaml b/Material.Dialog/Views/AlertDialog.axaml index 6635e5c..59ff89b 100644 --- a/Material.Dialog/Views/AlertDialog.axaml +++ b/Material.Dialog/Views/AlertDialog.axaml @@ -1,6 +1,7 @@  @@ -9,12 +10,14 @@ - - - - - + + + + + + + \ No newline at end of file diff --git a/Material.Dialog/Views/CustomDialog.axaml b/Material.Dialog/Views/CustomDialog.axaml index df0a18b..075db26 100644 --- a/Material.Dialog/Views/CustomDialog.axaml +++ b/Material.Dialog/Views/CustomDialog.axaml @@ -1,7 +1,8 @@  @@ -9,17 +10,19 @@ - - - - + + + + + - - - + + + + \ No newline at end of file diff --git a/Material.Dialog/Views/DatePickerDialog.axaml b/Material.Dialog/Views/DatePickerDialog.axaml index b7ae085..8846033 100644 --- a/Material.Dialog/Views/DatePickerDialog.axaml +++ b/Material.Dialog/Views/DatePickerDialog.axaml @@ -18,12 +18,9 @@ + + - diff --git a/Material.Dialog/Views/TextFieldDialog.axaml b/Material.Dialog/Views/TextFieldDialog.axaml index acf5320..22757dc 100644 --- a/Material.Dialog/Views/TextFieldDialog.axaml +++ b/Material.Dialog/Views/TextFieldDialog.axaml @@ -3,6 +3,7 @@ xmlns:wpf="clr-namespace:Material.Styles.Assists;assembly=Material.Styles" xmlns:viewModels="clr-namespace:Material.Dialog.ViewModels" xmlns:textField="clr-namespace:Material.Dialog.ViewModels.Elements.TextField" + xmlns:controls="clr-namespace:Material.Dialog.Controls" x:Class="Material.Dialog.Views.TextFieldDialog" x:DataType="viewModels:TextFieldDialogViewModel"> @@ -11,30 +12,32 @@ + + + + + - - - - - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/Material.Dialog/Views/TextFieldDialog.axaml.cs b/Material.Dialog/Views/TextFieldDialog.axaml.cs index 9f36e61..cfea30f 100644 --- a/Material.Dialog/Views/TextFieldDialog.axaml.cs +++ b/Material.Dialog/Views/TextFieldDialog.axaml.cs @@ -3,6 +3,7 @@ using Avalonia.Markup.Xaml; using Material.Dialog.Interfaces; using Material.Dialog.ViewModels; using System; +using Avalonia; using Avalonia.Controls.Presenters; using Avalonia.Threading; @@ -17,7 +18,7 @@ namespace Material.Dialog.Views Result = new TextFieldDialogResult(); InitializeComponent(); - + Closed += TextFieldDialog_Closed; Opened += TextFieldDialog_Opened; } diff --git a/Material.Dialog/Views/TimePickerDialog.axaml b/Material.Dialog/Views/TimePickerDialog.axaml index 100454e..ecfbd8c 100644 --- a/Material.Dialog/Views/TimePickerDialog.axaml +++ b/Material.Dialog/Views/TimePickerDialog.axaml @@ -6,6 +6,7 @@ xmlns:assists="clr-namespace:Material.Styles.Assists;assembly=Material.Styles" xmlns:viewModels="clr-namespace:Material.Dialog.ViewModels" xmlns:converters="clr-namespace:Material.Dialog.Converters" + xmlns:controls="clr-namespace:Material.Dialog.Controls" x:Class="Material.Dialog.Views.TimePickerDialog" SizeToContent="WidthAndHeight" Title="{CompiledBinding WindowTitle}" @@ -22,12 +23,18 @@ - - + + + + + + + + + + + + diff --git a/Material.Dialog/Views/TimePickerDialog.axaml.cs b/Material.Dialog/Views/TimePickerDialog.axaml.cs index d8f8d6c..748e81e 100644 --- a/Material.Dialog/Views/TimePickerDialog.axaml.cs +++ b/Material.Dialog/Views/TimePickerDialog.axaml.cs @@ -5,7 +5,6 @@ using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Markup.Xaml; -using Avalonia.Media; using Material.Dialog.Interfaces; using Material.Dialog.ViewModels; @@ -13,96 +12,23 @@ namespace Material.Dialog.Views { public class TimePickerDialog : Window, IDialogWindowResult, IHasNegativeResult { - //private bool PointerHoldingCell; - private TimePickerDialogViewModel viewModel; - private Carousel PART_PagesRoot; - private Grid CallerPanel1; - private Grid CallerPanel2; - private Stack Pointers; - private bool HoldingPointer => Pointers.Count >= 1; - - private void CreateCallerCells1() => CreateCallerCellsToPanel(CallerPanel1, 12, firstText: 12, decreaseShows: false); - private void CreateCallerCells2() => CreateCallerCellsToPanel(CallerPanel2, 60,padNumbers:true); - - private void CreateCallerCellsToPanel(Grid panel, int counts, float radius = 109, int firstText = 0, bool padNumbers = false, bool decreaseShows = true) - { - var offset = (Math.PI * 2) * 0.25; - var target = 0; - - for (var i = 0; i < counts; i++) - { - if (decreaseShows) - { - if (target == i) - { - target += 5; - } - else - continue; - } - - var r = (float) i / (float) counts * (Math.PI * 2) - offset; - var x = radius * Math.Cos(r); - var y = radius * Math.Sin(r); - - var v = (i == 0 ? firstText : i); - var text = padNumbers ? v.ToString("D2") : v.ToString(); - var cell = CreateCallerCell(x, y, text); - panel.Children.Add(cell); - } - } - - private Control CreateCallerCell(double x, double y, string text) - { - var root = new Border() - { - Classes = Classes.Parse("CallerCell"), - RenderTransform = new TranslateTransform(x, y), - Background = SolidColorBrush.Parse("Transparent"), - Child = new Grid() - { - Children = { - new Border() - { - Name = "PointerEnterFeedback", - }, - new TextBlock() - { - Text = text - } - } - } - }; - return root; - } - public DateTimePickerDialogResult Result { get; set; } public TimePickerDialog() { Result = new DateTimePickerDialogResult(); - Pointers = new Stack(); InitializeComponent(); - // Create decorations - CallerPanel1 = this.Get(nameof(CallerPanel1)); - CallerPanel2 = this.Get(nameof(CallerPanel2)); } public void AttachViewModel(TimePickerDialogViewModel vm) { this.DataContext = vm; - viewModel = vm; } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); - - PART_PagesRoot = this.Get("PART_PagesRoot"); - - CreateCallerCells1(); - CreateCallerCells2(); } public DateTimePickerDialogResult GetResult() => Result; @@ -110,55 +36,5 @@ namespace Material.Dialog.Views public void SetNegativeResult(DialogResult result) => Result.Result = result.GetResult; private void InitializeComponent() => AvaloniaXamlLoader.Load(this); - - private void CallerPanel_OnPointerPressed(object sender, PointerPressedEventArgs e) - { - Pointers.Push(e.Pointer); - var panel = sender as Control; - var pointer = e.GetPosition(panel); - CallerPanel_OnPointerPressOrMove(panel, pointer); - } - - private void CallerPanel_OnPointerMoved(object sender, PointerEventArgs e) - { - var panel = sender as Control; - var pointer = e.GetPosition(panel); - CallerPanel_OnPointerPressOrMove(panel, pointer); - } - - private void CallerPanel_OnPointerPressOrMove(Control panel, Point p) - { - if (HoldingPointer) - { - var radius = panel.Bounds.Width / 2; - - var radians = Math.Atan2(p.X - radius, p.Y - radius); - var degree = 360 - ((radians * 180 / Math.PI) + 180); - ProcessPick(degree); - } - } - - private void ProcessPick(double deg) - { - var mul = PART_PagesRoot.SelectedIndex == 1 ? 60 : 12; - - var v = (int)Math.Round(deg / 360 * mul); - - if (v == mul) - v = 0; - - if(PART_PagesRoot.SelectedIndex == 1) - viewModel.SecondField = (ushort)v; - else - viewModel.FirstField = (ushort)v; - } - - private void CallerPanel_OnPointerReleased(object sender, PointerReleasedEventArgs e) - { - Pointers.Pop(); - - if(!HoldingPointer) - PART_PagesRoot.Next(); - } } } From 64ff7a248e980ba199feeb611c7e271b9cf911ca Mon Sep 17 00:00:00 2001 From: appleneko2001 Date: Fri, 3 Jun 2022 05:13:06 +0900 Subject: [PATCH 28/92] Move all controls to Controls folder and improve them (Contains formatting) Add circle clock picker --- Material.Styles/Controls/Arc.cs | 118 ++++--- .../{Card.xaml.cs => Controls/Card.cs} | 3 +- Material.Styles/Controls/CircleClockPicker.cs | 287 ++++++++++++++++++ .../Controls/CircleClockPickerCell.cs | 60 ++++ .../ColorZone.cs} | 12 +- .../FloatingButton.cs} | 2 +- Material.Styles/Controls/MaterialUnderline.cs | 55 ++++ .../NavigationDrawer.cs} | 5 +- .../SideSheet.cs} | 11 +- .../SnackbarHost.cs} | 50 ++- 10 files changed, 506 insertions(+), 97 deletions(-) rename Material.Styles/{Card.xaml.cs => Controls/Card.cs} (88%) create mode 100644 Material.Styles/Controls/CircleClockPicker.cs create mode 100644 Material.Styles/Controls/CircleClockPickerCell.cs rename Material.Styles/{ColorZone.xaml.cs => Controls/ColorZone.cs} (55%) rename Material.Styles/{FloatingButton.xaml.cs => Controls/FloatingButton.cs} (97%) create mode 100644 Material.Styles/Controls/MaterialUnderline.cs rename Material.Styles/{NavigationDrawer.xaml.cs => Controls/NavigationDrawer.cs} (98%) rename Material.Styles/{SideSheet.xaml.cs => Controls/SideSheet.cs} (96%) rename Material.Styles/{SnackbarHost.xaml.cs => Controls/SnackbarHost.cs} (83%) diff --git a/Material.Styles/Controls/Arc.cs b/Material.Styles/Controls/Arc.cs index 0f5d768..263f00c 100644 --- a/Material.Styles/Controls/Arc.cs +++ b/Material.Styles/Controls/Arc.cs @@ -1,40 +1,41 @@ using System; -using System.Globalization; -using System.Text; using Avalonia; using Avalonia.Controls; using Avalonia.Media; -using Avalonia.Threading; namespace Material.Styles.Controls { + /// + /// High-precision arc control. + /// Be remember, it takes a lot performance than avaloniaUI default arc control! + /// public class Arc : Control { static Arc() { - AffectsRender(ArcBrushProperty, - StrokeProperty, + AffectsRender(StrokeProperty, + StrokeThicknessProperty, StartAngleProperty, SweepAngleProperty); } - public IBrush ArcBrush - { - get => GetValue(ArcBrushProperty); - set => SetValue(ArcBrushProperty, value); - } - - public readonly static StyledProperty ArcBrushProperty = - AvaloniaProperty.Register(nameof(ArcBrush), new SolidColorBrush(Avalonia.Media.Colors.White)); - - public double Stroke + public IBrush Stroke { get => GetValue(StrokeProperty); set => SetValue(StrokeProperty, value); } - public readonly static StyledProperty StrokeProperty = - AvaloniaProperty.Register(nameof(Stroke)); + public readonly static StyledProperty StrokeProperty = + AvaloniaProperty.Register(nameof(Stroke)); + + public double StrokeThickness + { + get => GetValue(StrokeThicknessProperty); + set => SetValue(StrokeThicknessProperty, value); + } + + public readonly static StyledProperty StrokeThicknessProperty = + AvaloniaProperty.Register(nameof(StrokeThickness)); public double StartAngle { @@ -52,69 +53,86 @@ namespace Material.Styles.Controls } public static readonly StyledProperty SweepAngleProperty = - AvaloniaProperty.Register(nameof(SweepAngle), 90); + AvaloniaProperty.Register(nameof(SweepAngle)); public override void Render(DrawingContext context) { - var offsetStroke = 0.5; - var o = Stroke + offsetStroke; + const double offsetStroke = 0.5; + var o = StrokeThickness + offsetStroke; // Create main circle for draw circle var mainCircle = new EllipseGeometry(new Rect(o / 2, o / 2, Bounds.Width - o, Bounds.Height - o)); - var paint = new Pen(ArcBrush, Stroke); + var paint = new Pen(Stroke, StrokeThickness); // Push generated clip geometry for clipping circle figure using (context.PushGeometryClip(GetClip())) { - context.DrawGeometry(SolidColorBrush.Parse("Transparent"), paint, mainCircle); + context.DrawGeometry(Brushes.Transparent, paint, mainCircle); } - - Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background); } - // TODO: Optimal clip geometry generator + // not-TO-DO: Optimal clip geometry generator + // I gave up, using avaloniaUI default arc instead now:( + // but I give you choice to use this "High-precision" arc if you needed it in ProgressBar btw + // only you have to do is define a custom style with replacing default circular class of ProgressBar + // or you can use it anywhere + + // Well I did some small changes here to not parsing figure string, + // but build figure from stream geometry context. + // This changes may prevent performance waste a little bit. + // Clip geometry generator private StreamGeometry GetClip() { - var offset = StartAngle - 90; + var offset = StartAngle; var w = Bounds.Width; var h = Bounds.Height; var halfW = w / 2; var halfH = h / 2; + + var geometry = new StreamGeometry(); + + var sweep = SweepAngle; - var sweep = (offset + SweepAngle) / 360; + if (sweep == 0) + return geometry; + + using var ctx = geometry.Open(); - var path = new StringBuilder($"M {halfW.ToString(CultureInfo.InvariantCulture)} {halfH.ToString(CultureInfo.InvariantCulture)}"); - - int length = 24; - - for (int i = 0; i < length; i++) + // Center point + ctx.BeginFigure(new Point(halfW, halfH), true); + + const int len = 24; + + for (var i = 0; i < len; i++) { - var limit = offset / 360 + i / (double)length; + var l = offset + sweep * i / len; - if (limit > sweep) - break; - - var r2 = limit * (Math.PI * 2); - var x2 = halfW + Math.Round(halfW * Math.Cos(r2), 4); - var y2 = halfH + Math.Round(halfH * Math.Sin(r2), 4); - - path.Append($" {x2.ToString(CultureInfo.InvariantCulture)} {y2.ToString(CultureInfo.InvariantCulture)}"); + ctx.LineTo(DegToPoint(l, halfW, halfH)); } - - var r3 = sweep * (Math.PI * 2); - var x3 = halfW + Math.Round(halfW * Math.Cos(r3), 4); - var y3 = halfH + Math.Round(halfH * Math.Sin(r3), 4); - - path.Append($" {x3.ToString(CultureInfo.InvariantCulture)} {y3.ToString(CultureInfo.InvariantCulture)}"); + + ctx.LineTo(DegToPoint(offset + sweep, halfW, halfH)); + + ctx.EndFigure(true); + return geometry; + } - path.Append(" Z"); - var result = path.ToString().Replace(',', '.'); - return StreamGeometry.Parse(result); + private const double Rad = Math.PI / 180d; + + private static double Round(double v) => Math.Round(v); + + private static Point DegToPoint(double deg, double halfW, double halfH) + { + var rad = deg * Rad; + + var x = halfW + Round(halfW * Math.Cos(rad)); + var y = halfH + Round(halfH * Math.Sin(rad)); + + return new Point(x, y); } } } \ No newline at end of file diff --git a/Material.Styles/Card.xaml.cs b/Material.Styles/Controls/Card.cs similarity index 88% rename from Material.Styles/Card.xaml.cs rename to Material.Styles/Controls/Card.cs index e13c132..0102ca0 100644 --- a/Material.Styles/Card.xaml.cs +++ b/Material.Styles/Controls/Card.cs @@ -1,8 +1,7 @@ using Avalonia; using Avalonia.Controls; -using Avalonia.Controls.Primitives; -namespace Material.Styles { +namespace Material.Styles.Controls { public class Card : ContentControl { public static readonly StyledProperty InsideClippingProperty = AvaloniaProperty.Register(nameof(InsideClipping), true); diff --git a/Material.Styles/Controls/CircleClockPicker.cs b/Material.Styles/Controls/CircleClockPicker.cs new file mode 100644 index 0000000..700ffe7 --- /dev/null +++ b/Material.Styles/Controls/CircleClockPicker.cs @@ -0,0 +1,287 @@ +using System; +using System.Collections.Generic; +using System.Reactive.Disposables; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Input; +using Avalonia.Media; +using Panel = Avalonia.Controls.Panel; + +namespace Material.Styles.Controls +{ + public class CircleClockPicker : TemplatedControl + { + public static readonly DirectProperty ValueProperty = + AvaloniaProperty.RegisterDirect(nameof(Value), + o => o.Value, (o, v) => o.Value = v); + + public static readonly StyledProperty MinimumProperty = + AvaloniaProperty.Register(nameof(Minimum)); + + public static readonly StyledProperty MaximumProperty = + AvaloniaProperty.Register(nameof(Maximum)); + + public static readonly StyledProperty StepFrequencyProperty = + AvaloniaProperty.Register(nameof(StepFrequency)); + + public static readonly StyledProperty FirstLabelOverrideProperty = + AvaloniaProperty.Register(nameof(FirstLabelOverride)); + + public static readonly StyledProperty RadiusMultiplierProperty = + AvaloniaProperty.Register(nameof(RadiusMultiplier)); + + public int Value + { + get => _value; + set + { + SetAndRaise(ValueProperty, ref _value, value); + UpdateVisual(value); + } + } + + public int Minimum + { + get => GetValue(MinimumProperty); + set => SetValue(MinimumProperty, value); + } + + public int Maximum + { + get => GetValue(MaximumProperty); + set => SetValue(MaximumProperty, value); + } + + public int StepFrequency + { + get => GetValue(StepFrequencyProperty); + set => SetValue(StepFrequencyProperty, value); + } + + public string? FirstLabelOverride + { + get => GetValue(FirstLabelOverrideProperty); + set => SetValue(FirstLabelOverrideProperty, value); + } + + public double RadiusMultiplier + { + get => GetValue(RadiusMultiplierProperty); + set => SetValue(RadiusMultiplierProperty, value); + } + + public event EventHandler? AfterDrag; + + static CircleClockPicker() + { + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + + _subscription?.Dispose(); + _subscription = null; + + var pointer = e.NameScope.Find("PART_Pointer"); + var canvas = e.NameScope.Find("PART_CellPanel"); + var pointerPin = e.NameScope.Find("PART_PointerPin"); + + _pointer = pointer; + _pointerPin = pointerPin; + _cellPanel = canvas; + + _subscription = new CompositeDisposable + { + MinimumProperty.Changed.Subscribe(OnNext), + MaximumProperty.Changed.Subscribe(OnNext), + StepFrequencyProperty.Changed.Subscribe(OnNext), + FirstLabelOverrideProperty.Changed.Subscribe(OnNext), + RadiusMultiplierProperty.Changed.Subscribe(OnNext), + BoundsProperty.Changed.Subscribe(OnCanvasResize) + }; + + UpdateCellPanel(); + AdjustPointer(); + UpdateVisual(_value); + } + + private void OnCanvasResize(AvaloniaPropertyChangedEventArgs obj) + { + if (!ReferenceEquals(obj.Sender, _cellPanel)) + return; + + UpdateCellPanel(); + AdjustPointer(); + } + + private void OnNext(EventArgs a) + { + UpdateCellPanel(); + AdjustPointer(); + } + + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + base.OnPointerPressed(e); + + _isDragging = true; + + ProcessPointerEvent(e.GetPosition(this)); + } + + protected override void OnPointerMoved(PointerEventArgs e) + { + base.OnPointerMoved(e); + + if (!_isDragging) + return; + + ProcessPointerEvent(e.GetPosition(this)); + } + + protected override void OnPointerReleased(PointerReleasedEventArgs e) + { + base.OnPointerReleased(e); + + _isDragging = false; + AfterDrag?.Invoke(this, EventArgs.Empty); + } + + private bool _isDragging; + private Control? _pointer; + private Control? _pointerPin; + private Panel? _cellPanel; + private readonly Dictionary _cachedAccessors = new (); + private IDisposable? _subscription; + + private int _value; + + private void ProcessPointerEvent(Point point) + { + var halfSize = (float) (Bounds.Width / 2); + var rad = (float) Math.Atan2(point.Y - halfSize, point.X - halfSize); + var degrees = rad * 180 / Math.PI + 90; + + if(degrees < 0) + degrees += 360; + + if(degrees > 360) + degrees -= 360; + + // degree to value + var value = (int) Math.Round(degrees / 360 * (Maximum + 1 - Minimum) + Minimum); + + if(value == Maximum + 1) + value = Minimum; + + if(!_cachedAccessors.TryGetValue(value, out var c)) + return; + + Value = c.Value; + } + + private void UpdateVisual(int v) + { + if(!_cachedAccessors.TryGetValue(v, out var cell)) + return; + + foreach (var c in _cachedAccessors.Values) + { + c.IsSelected = false; + + if (!ReferenceEquals(c, cell)) + continue; + + c.IsSelected = true; + } + + if(_pointer == null) + return; + + var degrees = (v - 90f) * (360f / (Maximum + 1 - Minimum)); + + var transform = new RotateTransform(degrees); + _pointer.RenderTransform = transform; + } + + private void UpdateCellPanel() + { + if(_cellPanel == null) + return; + + var step = StepFrequency; + var min = Minimum; + var max = Maximum; + + var radiusMultiplier = RadiusMultiplier; + + _cachedAccessors.Clear(); + _cellPanel.Children.Clear(); + + void ArrangeCell(CircleClockPickerCell cell, double degree) + { + var canvasBounds = _cellPanel.Bounds; + + var w = canvasBounds.Width; + var h = canvasBounds.Height; + + var hW = w / 2; + var hH = h / 2; + + var rad = (float) ((degree - 90) * Math.PI / 180); + + var x = (float) (hW * radiusMultiplier * Math.Cos(rad)) + hW; + var y = (float) (hH * radiusMultiplier * Math.Sin(rad)) + hH; + + cell.RenderTransform = new TranslateTransform(x, y); + } + + float GetAngle(int value) + { + var degrees = (value - min) * (360f / (max + 1 - min)); + return degrees; + } + + for (var i = min; i <= max; i++) + { + var cell = new CircleClockPickerCell + { + Value = i + }; + + if(step > 0) + { + if (i % step == 0) + cell.IsDot = false; + } + + cell.Content = i.ToString(); + + if(FirstLabelOverride != null && i == min) + cell.Content = FirstLabelOverride; + + _cellPanel.Children.Add(cell); + + ArrangeCell(cell, GetAngle(i)); + + _cachedAccessors.Add(i, cell); + } + + UpdateVisual(Value); + } + + private void AdjustPointer() + { + if(_pointerPin == null) + return; + + if(_cellPanel == null) + return; + + var radius = _cellPanel.Bounds.Width / 2; + _pointerPin.Height = radius * RadiusMultiplier; + } + } +} \ No newline at end of file diff --git a/Material.Styles/Controls/CircleClockPickerCell.cs b/Material.Styles/Controls/CircleClockPickerCell.cs new file mode 100644 index 0000000..b4b1b8c --- /dev/null +++ b/Material.Styles/Controls/CircleClockPickerCell.cs @@ -0,0 +1,60 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Metadata; + +namespace Material.Styles.Controls +{ + [PseudoClasses(":selected", ":dot")] + public class CircleClockPickerCell : ContentControl + { + public static readonly StyledProperty IsSelectedProperty = + AvaloniaProperty.Register(nameof(IsSelected)); + + public static readonly StyledProperty IsDotProperty = + AvaloniaProperty.Register(nameof(IsDot)); + + public static readonly DirectProperty ValueProperty = + AvaloniaProperty.RegisterDirect(nameof(Value), o => o.Value, (o, v) => o.Value = v); + + public bool IsSelected + { + get => GetValue(IsSelectedProperty); + set => SetValue(IsSelectedProperty, value); + } + + public bool IsDot + { + get => GetValue(IsDotProperty); + set => SetValue(IsDotProperty, value); + } + + public int Value + { + get => _value; + set => SetAndRaise(ValueProperty, ref _value, value); + } + + private int _value; + + static CircleClockPickerCell() + { + AffectsArrange(IsDotProperty); + AffectsRender(IsSelectedProperty); + + IsSelectedProperty.Changed.AddClassHandler(PropertyChangedHandler); + IsDotProperty.Changed.AddClassHandler(PropertyChangedHandler); + } + + private static void PropertyChangedHandler(CircleClockPickerCell t, + AvaloniaPropertyChangedEventArgs a) + { + t.UpdatePseudoClasses(); + } + + private void UpdatePseudoClasses() + { + PseudoClasses.Set(":selected", IsSelected); + PseudoClasses.Set(":dot", IsDot); + } + } +} \ No newline at end of file diff --git a/Material.Styles/ColorZone.xaml.cs b/Material.Styles/Controls/ColorZone.cs similarity index 55% rename from Material.Styles/ColorZone.xaml.cs rename to Material.Styles/Controls/ColorZone.cs index eb71854..3631652 100644 --- a/Material.Styles/ColorZone.xaml.cs +++ b/Material.Styles/Controls/ColorZone.cs @@ -1,7 +1,7 @@ using Avalonia; using Avalonia.Controls; -namespace Material.Styles { +namespace Material.Styles.Controls { public enum ColorZoneMode { Standard, @@ -15,16 +15,6 @@ namespace Material.Styles { Custom } public class ColorZone : ContentControl { - public static readonly StyledProperty CornerRadiusProperty = - AvaloniaProperty.Register(nameof(CornerRadius)); - - /// - /// Gets or sets the radius of the border rounded corners. - /// - public CornerRadius CornerRadius { - get => GetValue(CornerRadiusProperty); - set => SetValue(CornerRadiusProperty, value); - } public static readonly StyledProperty ModeProperty = AvaloniaProperty.Register(nameof(Mode)); diff --git a/Material.Styles/FloatingButton.xaml.cs b/Material.Styles/Controls/FloatingButton.cs similarity index 97% rename from Material.Styles/FloatingButton.xaml.cs rename to Material.Styles/Controls/FloatingButton.cs index 61cccd0..4f52cff 100644 --- a/Material.Styles/FloatingButton.xaml.cs +++ b/Material.Styles/Controls/FloatingButton.cs @@ -1,7 +1,7 @@ using Avalonia; using Avalonia.Controls; -namespace Material.Styles +namespace Material.Styles.Controls { public class FloatingButton : Button { diff --git a/Material.Styles/Controls/MaterialUnderline.cs b/Material.Styles/Controls/MaterialUnderline.cs new file mode 100644 index 0000000..972cd3b --- /dev/null +++ b/Material.Styles/Controls/MaterialUnderline.cs @@ -0,0 +1,55 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; + +namespace Material.Styles.Controls { + public class MaterialUnderline : ContentControl { + /// + /// Defines the property. + /// + public static readonly StyledProperty IdleBrushProperty = + AvaloniaProperty.Register(nameof(IdleBrush)); + + public IBrush IdleBrush + { + get => GetValue(IdleBrushProperty); + set => SetValue(IdleBrushProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty ActiveBrushProperty = + AvaloniaProperty.Register(nameof(ActiveBrush)); + + public IBrush ActiveBrush + { + get => GetValue(ActiveBrushProperty); + set => SetValue(ActiveBrushProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsActiveProperty = + AvaloniaProperty.Register(nameof(IsActive)); + + public bool IsActive + { + get => GetValue(IsActiveProperty); + set => SetValue(IsActiveProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsHoveredProperty = + AvaloniaProperty.Register(nameof(IsHovered)); + + public bool IsHovered + { + get => GetValue(IsHoveredProperty); + set => SetValue(IsHoveredProperty, value); + } + } +} \ No newline at end of file diff --git a/Material.Styles/NavigationDrawer.xaml.cs b/Material.Styles/Controls/NavigationDrawer.cs similarity index 98% rename from Material.Styles/NavigationDrawer.xaml.cs rename to Material.Styles/Controls/NavigationDrawer.cs index 4e8b45c..a3d6454 100644 --- a/Material.Styles/NavigationDrawer.xaml.cs +++ b/Material.Styles/Controls/NavigationDrawer.cs @@ -1,12 +1,12 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Metadata; +using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Interactivity; using Avalonia.Metadata; -using Avalonia.Controls.Primitives; -namespace Material.Styles +namespace Material.Styles.Controls { [PseudoClasses(":open", ":closed", ":left", ":right")] public class NavigationDrawer : ContentControl @@ -109,6 +109,7 @@ namespace Material.Styles drawer.UpdatePseudoClasses(); } + // ReSharper disable once InconsistentNaming private Border? PART_Scrim; protected override void OnApplyTemplate(TemplateAppliedEventArgs e) diff --git a/Material.Styles/SideSheet.xaml.cs b/Material.Styles/Controls/SideSheet.cs similarity index 96% rename from Material.Styles/SideSheet.xaml.cs rename to Material.Styles/Controls/SideSheet.cs index b3c3cda..a80a814 100644 --- a/Material.Styles/SideSheet.xaml.cs +++ b/Material.Styles/Controls/SideSheet.cs @@ -1,13 +1,13 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Metadata; +using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Interactivity; using Avalonia.Metadata; -using Avalonia.Controls.Primitives; using Material.Styles.Enums; -namespace Material.Styles +namespace Material.Styles.Controls { // TODO: mobile variant [PseudoClasses(":open", ":closed", ":left", ":right", ":mobile")] @@ -58,8 +58,8 @@ namespace Material.Styles /// public Thickness SideSheetPadding { - get { return GetValue(SideSheetPaddingProperty); } - set { SetValue(SideSheetPaddingProperty, value); } + get => GetValue(SideSheetPaddingProperty); + set => SetValue(SideSheetPaddingProperty, value); } /// @@ -136,6 +136,7 @@ namespace Material.Styles // Controls + // ReSharper disable once InconsistentNaming private Border? PART_Scrim; protected override void OnApplyTemplate(TemplateAppliedEventArgs e) @@ -149,7 +150,7 @@ namespace Material.Styles if (e.NameScope.Find("PART_CloseButton") is Button button) { - button.Click += (sender, args) => SideSheetOpened = false; + button.Click += (_, _) => SideSheetOpened = false; } base.OnApplyTemplate(e); diff --git a/Material.Styles/SnackbarHost.xaml.cs b/Material.Styles/Controls/SnackbarHost.cs similarity index 83% rename from Material.Styles/SnackbarHost.xaml.cs rename to Material.Styles/Controls/SnackbarHost.cs index 3166f88..c698cbc 100644 --- a/Material.Styles/SnackbarHost.xaml.cs +++ b/Material.Styles/Controls/SnackbarHost.cs @@ -5,17 +5,16 @@ using System.Linq; using System.Timers; using Avalonia; using Avalonia.Controls; -using Avalonia.LogicalTree; using Avalonia.Controls.Primitives; using Avalonia.Layout; using Avalonia.Threading; using Material.Styles.Models; -namespace Material.Styles +namespace Material.Styles.Controls { public class SnackbarHost : ContentControl { - private static readonly Dictionary _snackbarHostDictionary; + private static readonly Dictionary SnackbarHostDictionary; private readonly ObservableCollection _snackbars; public ObservableCollection SnackbarModels => _snackbars; @@ -32,22 +31,20 @@ namespace Material.Styles { SetValue(HostNameProperty, value); - if (_snackbarHostDictionary.ContainsValue(this)) + if (!SnackbarHostDictionary.ContainsValue(this)) + return; + + KeyValuePair? target = null; + foreach (var host in SnackbarHostDictionary + .Where(host => ReferenceEquals(host.Value, this))) { - KeyValuePair? target = null; - foreach (var host in _snackbarHostDictionary) - { - if (ReferenceEquals(host.Value, this)) - { - target = host; - break; - } - } + target = host; + break; + } - if (target.HasValue) - { - _snackbarHostDictionary.Remove(target.Value.Key); - } + if (target.HasValue) + { + SnackbarHostDictionary.Remove(target.Value.Key); } } else @@ -79,7 +76,7 @@ namespace Material.Styles static SnackbarHost() { //_snackbarHosts = new HashSet(); - _snackbarHostDictionary = new Dictionary(); + SnackbarHostDictionary = new Dictionary(); } public SnackbarHost() @@ -90,11 +87,11 @@ namespace Material.Styles private static string GetFirstHostName() { - if (_snackbarHostDictionary is null) + if (SnackbarHostDictionary is null) // THIS IS IMPOSSIBLE TO HAPPEN! But I kept this for any reasons. throw new NullReferenceException("Snackbar hosts pool is not initialized!"); - return _snackbarHostDictionary.First().Key; + return SnackbarHostDictionary.First().Key; } private static SnackbarHost GetHost(string name) @@ -102,7 +99,7 @@ namespace Material.Styles if (name is null) throw new ArgumentNullException(nameof(name)); - var result = _snackbarHostDictionary[name]; + var result = SnackbarHostDictionary[name]; return result; } @@ -164,7 +161,8 @@ namespace Material.Styles /// snackbar data model. /// the snackbar host that you wanted to use. /// the priority of current task. - public static void Remove(SnackbarModel model, string targetHost = null, DispatcherPriority priority = DispatcherPriority.Normal) + public static void Remove(SnackbarModel model, string targetHost = null, + DispatcherPriority priority = DispatcherPriority.Normal) { if (string.IsNullOrEmpty(targetHost)) targetHost = GetFirstHostName(); @@ -190,16 +188,16 @@ namespace Material.Styles protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { - _snackbarHostDictionary.Add(HostName, this); + SnackbarHostDictionary.Add(HostName, this); base.OnAttachedToVisualTree(e); } - protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) { - _snackbarHostDictionary.Remove(HostName); + SnackbarHostDictionary.Remove(HostName); - base.OnDetachedFromLogicalTree(e); + base.OnDetachedFromVisualTree(e); } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) From 0c3c74995873c1262593b6951bd4b18c4a118f1c Mon Sep 17 00:00:00 2001 From: appleneko2001 Date: Fri, 3 Jun 2022 05:15:42 +0900 Subject: [PATCH 29/92] Move TickBar.cs to Controls folder and improve it (Add more properties and update rendering) --- Material.Styles/Controls/TickBar.cs | 376 ++++++++++++++++++++++++++++ Material.Styles/TickBar.cs | 282 --------------------- 2 files changed, 376 insertions(+), 282 deletions(-) create mode 100644 Material.Styles/Controls/TickBar.cs delete mode 100644 Material.Styles/TickBar.cs diff --git a/Material.Styles/Controls/TickBar.cs b/Material.Styles/Controls/TickBar.cs new file mode 100644 index 0000000..f476ce0 --- /dev/null +++ b/Material.Styles/Controls/TickBar.cs @@ -0,0 +1,376 @@ +using System; +using Avalonia; +using Avalonia.Collections; +using Avalonia.Controls; +using Avalonia.Layout; +using Avalonia.Media; +using Avalonia.Utilities; + +// ReSharper disable MemberCanBePrivate.Global + +namespace Material.Styles.Controls +{ + + /// + /// An element that is used for drawing 's Ticks. + ///
+ /// Those code taken from AvaloniaUI and modified. + ///
+ public class TickBar : Control + { + static TickBar() + { + AffectsRender(IsDirectionReversedProperty, + InactiveBrushProperty, + ActiveBrushProperty, + ReservedSpaceProperty, + MaximumProperty, + MinimumProperty, + ValueProperty, + OrientationProperty, + ThicknessTickProperty, + TickFrequencyProperty, + TicksProperty); + } + + public static readonly StyledProperty IsDirectionReversedProperty = + AvaloniaProperty.Register(nameof(IsDirectionReversed)); + + public bool IsDirectionReversed + { + get => GetValue(IsDirectionReversedProperty); + set => SetValue(IsDirectionReversedProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty InactiveBrushProperty = + AvaloniaProperty.Register(nameof(InactiveBrush)); + + /// + /// Brush used to draw the TickBar's Ticks on inactive track. + /// + public IBrush InactiveBrush + { + get => GetValue(InactiveBrushProperty); + set => SetValue(InactiveBrushProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty ActiveBrushProperty = + AvaloniaProperty.Register(nameof(ActiveBrush)); + + /// + /// Brush used to draw the TickBar's Ticks on active track. + /// + public IBrush ActiveBrush + { + get => GetValue(ActiveBrushProperty); + set => SetValue(ActiveBrushProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty MinimumProperty = + AvaloniaProperty.Register(nameof(Minimum)); + + /// + /// Logical position where the Minimum Tick will be drawn + /// + public double Minimum + { + get => GetValue(MinimumProperty); + set => SetValue(MinimumProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty MaximumProperty = + AvaloniaProperty.Register(nameof(Maximum)); + + /// + /// Logical position where the Maximum Tick will be drawn + /// + public double Maximum + { + get => GetValue(MaximumProperty); + set => SetValue(MaximumProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty ValueProperty = + AvaloniaProperty.Register(nameof(Value)); + + /// + /// Logical position where the Maximum Tick will be drawn + /// + public double Value + { + get => GetValue(ValueProperty); + set => SetValue(ValueProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty ThicknessTickProperty = + AvaloniaProperty.Register(nameof(ThicknessTick)); + + public double ThicknessTick + { + get => GetValue(ThicknessTickProperty); + set => SetValue(ThicknessTickProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty TickFrequencyProperty = + AvaloniaProperty.Register(nameof(TickFrequency)); + + /// + /// TickFrequency property defines how the tick will be drawn. + /// + public double TickFrequency + { + get => GetValue(TickFrequencyProperty); + set => SetValue(TickFrequencyProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty OrientationProperty = + AvaloniaProperty.Register(nameof(Orientation)); + + /// + /// TickBar parent's orientation. + /// + public Orientation Orientation + { + get => GetValue(OrientationProperty); + set => SetValue(OrientationProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty> TicksProperty = + AvaloniaProperty.Register>(nameof(Ticks)); + + /// + /// The Ticks property contains collection of value of type Double which + /// are the logical positions use to draw the ticks. + /// The property value is a . + /// + public AvaloniaList Ticks + { + get => GetValue(TicksProperty); + set => SetValue(TicksProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty ReservedSpaceProperty = + AvaloniaProperty.Register(nameof(ReservedSpace)); + + /// + /// TickBar will use ReservedSpaceProperty for horizontal orientation spacing or + /// vertical orientation spacing. + /// The space on both sides of TickBar is half of specified ReservedSpace. + /// This property has type of . + /// + public Rect ReservedSpace + { + get => GetValue(ReservedSpaceProperty); + set => SetValue(ReservedSpaceProperty, value); + } + + public override void Render(DrawingContext dc) + { + var size = new Size(Bounds.Width, Bounds.Height); + var range = Maximum - Minimum; + double logicalToPhysical; + Point startPoint, endPoint; + var rSpace = Orientation == Orientation.Horizontal ? ReservedSpace.Width : ReservedSpace.Height; + var left = Value; + var reverse = IsDirectionReversed; + + // Take Thumb size in to account + var halfReservedSpace = rSpace * 0.5; + + var thicknessTick = ThicknessTick; + + switch (Orientation) + { + case Orientation.Horizontal: + if (MathUtilities.GreaterThanOrClose(rSpace, size.Width)) + return; + + var hHeight = size.Height / 2; + size = new Size(size.Width - rSpace, size.Height); + startPoint = new Point(halfReservedSpace, hHeight); + endPoint = new Point(halfReservedSpace + size.Width, hHeight); + logicalToPhysical = size.Width / range; + break; + + case Orientation.Vertical: + if (MathUtilities.GreaterThanOrClose(rSpace, size.Height)) + return; + + var hWidth = size.Width / 2; + size = new Size(size.Width, size.Height - rSpace); + startPoint = new Point(hWidth, size.Height + halfReservedSpace); + endPoint = new Point(hWidth, halfReservedSpace); + logicalToPhysical = size.Height / range * -1; + break; + + default: + throw new ArgumentOutOfRangeException(); + } + + // Invert direction of the ticks + if (reverse) + { + logicalToPhysical *= -1; + + // swap startPoint & endPoint + (startPoint, endPoint) = (endPoint, startPoint); + } + + var inactiveBrush = InactiveBrush.ToImmutable(); + var activeBrush = ActiveBrush.ToImmutable(); + + void DrawTick(IBrush brush, Point centerPoint, double radius) + { + dc.DrawEllipse(brush, null, centerPoint, radius, radius); + } + + IBrush PickBrushByRange(double c) + { + if (c < left) + return reverse ? inactiveBrush : activeBrush; + + return reverse ? activeBrush : inactiveBrush; + } + + switch (Orientation) + { + // Horizontal + case Orientation.Horizontal: + { + // Reduce tick interval if it is more than would be visible on the screen + var interval = TickFrequency; + if (interval > 0.0) + { + var minInterval = (Maximum - Minimum) / size.Width; + if (interval < minInterval) + interval = minInterval; + } + + // Draw Min & Max tick + DrawTick(PickBrushByRange(0), startPoint, thicknessTick); + DrawTick(PickBrushByRange(range), endPoint, thicknessTick); + + // This property is rarely set so let's try to avoid the GetValue + // caching of the mutable default value + // ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract + var ticks = Ticks ?? null; + + // Draw ticks using specified Ticks collection + if (ticks?.Count > 0) + { + for (var i = 0; i < ticks.Count; i++) + { + var tick = ticks[i]; + if (MathUtilities.LessThanOrClose(tick, Minimum) || + MathUtilities.GreaterThanOrClose(tick, Maximum)) + continue; + + var adjustedTick = tick - Minimum; + + var x = adjustedTick * logicalToPhysical + startPoint.X; + var point = new Point(x, startPoint.Y); + + DrawTick(PickBrushByRange(tick), point, thicknessTick); + } + } + // Draw ticks using specified TickFrequency + else if (interval > 0.0) + { + for (var i = interval; i < range; i += interval) + { + var x = i * logicalToPhysical + startPoint.X; + var point = new Point(x, startPoint.Y); + + DrawTick(PickBrushByRange(i), point, thicknessTick); + } + } + } break; + + // Vertical + case Orientation.Vertical: + { + // Reduce tick interval if it is more than would be visible on the screen + var interval = TickFrequency; + if (interval > 0.0) + { + var minInterval = (Maximum - Minimum) / size.Height; + if (interval < minInterval) + interval = minInterval; + } + + // Draw Min & Max tick + DrawTick(PickBrushByRange(0), startPoint, thicknessTick); + DrawTick(PickBrushByRange(range), endPoint, thicknessTick); + + // This property is rarely set so let's try to avoid the GetValue + // caching of the mutable default value + // ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract + var ticks = Ticks ?? null; + + // Draw ticks using specified Ticks collection + if (ticks?.Count > 0) + { + for (var i = 0; i < ticks.Count; i++) + { + var tick = ticks[i]; + if (MathUtilities.LessThanOrClose(tick, Minimum) || + MathUtilities.GreaterThanOrClose(tick, Maximum)) + continue; + + var adjustedTick = ticks[i] - Minimum; + + var y = adjustedTick * logicalToPhysical + startPoint.Y; + var point = new Point(startPoint.X, y); + + DrawTick(PickBrushByRange(tick), point, thicknessTick); + } + } + // Draw ticks using specified TickFrequency + else if (interval > 0.0) + { + for (var i = interval; i < range; i += interval) + { + var y = i * logicalToPhysical + startPoint.Y; + var point = new Point(startPoint.X, y); + + DrawTick(PickBrushByRange(i), point, thicknessTick); + } + } + } break; + + default: + throw new ArgumentOutOfRangeException(); + } + } + } +} diff --git a/Material.Styles/TickBar.cs b/Material.Styles/TickBar.cs deleted file mode 100644 index ce164d2..0000000 --- a/Material.Styles/TickBar.cs +++ /dev/null @@ -1,282 +0,0 @@ -using Avalonia.Utilities; -using System; -using System.Collections.Generic; -using Avalonia; -using System.Text; -using Avalonia.Controls; -using Avalonia.Media; -using Avalonia.Layout; -using Avalonia.Collections; -using Material.Styles.Assists; - -namespace Material.Styles -{ - - /// - /// An element that is used for drawing 's Ticks. - ///
- /// Those code taken from AvaloniaUI and modified. - ///
- public class TickBar : Control - { - static TickBar() - { - AffectsRender(FillProperty, - ReservedSpaceProperty, - MaximumProperty, - MinimumProperty, - OrientationProperty, - TickFrequencyProperty, - TicksProperty); - } - - public TickBar() : base() - { - } - - /// - /// Defines the property. - /// - public static readonly StyledProperty FillProperty = - AvaloniaProperty.Register(nameof(Fill)); - - /// - /// Brush used to fill the TickBar's Ticks. - /// - public IBrush Fill - { - get => GetValue(FillProperty); - set => SetValue(FillProperty, value); - } - - /// - /// Defines the property. - /// - public static readonly StyledProperty MinimumProperty = - AvaloniaProperty.Register(nameof(Minimum), 0d); - - /// - /// Logical position where the Minimum Tick will be drawn - /// - public double Minimum - { - get => GetValue(MinimumProperty); - set => SetValue(MinimumProperty, value); - } - - /// - /// Defines the property. - /// - public static readonly StyledProperty MaximumProperty = - AvaloniaProperty.Register(nameof(Maximum), 0d); - - /// - /// Logical position where the Maximum Tick will be drawn - /// - public double Maximum - { - get => GetValue(MaximumProperty); - set => SetValue(MaximumProperty, value); - } - - /// - /// Defines the property. - /// - public static readonly StyledProperty TickFrequencyProperty = - AvaloniaProperty.Register(nameof(TickFrequency), 0d); - - /// - /// TickFrequency property defines how the tick will be drawn. - /// - public double TickFrequency - { - get => GetValue(TickFrequencyProperty); - set => SetValue(TickFrequencyProperty, value); - } - - /// - /// Defines the property. - /// - public static readonly StyledProperty OrientationProperty = - AvaloniaProperty.Register(nameof(Orientation)); - - /// - /// TickBar parent's orientation. - /// - public Orientation Orientation - { - get => GetValue(OrientationProperty); - set => SetValue(OrientationProperty, value); - } - - /// - /// Defines the property. - /// - public static readonly StyledProperty> TicksProperty = - AvaloniaProperty.Register>(nameof(Ticks)); - - /// - /// The Ticks property contains collection of value of type Double which - /// are the logical positions use to draw the ticks. - /// The property value is a . - /// - public AvaloniaList Ticks - { - get => GetValue(TicksProperty); - set => SetValue(TicksProperty, value); - } - - /// - /// Defines the property. - /// - public static readonly StyledProperty ReservedSpaceProperty = - AvaloniaProperty.Register(nameof(ReservedSpace)); - - /// - /// TickBar will use ReservedSpaceProperty for left and right spacing (for horizontal orientation) or - /// top and bottom spacing (for vertical orienation). - /// The space on both sides of TickBar is half of specified ReservedSpace. - /// This property has type of . - /// - public Rect ReservedSpace - { - get => GetValue(ReservedSpaceProperty); - set => SetValue(ReservedSpaceProperty, value); - } - - /// - /// Draw ticks. - /// Ticks can be draw in 8 different ways depends on Placement property and IsDirectionReversed property. - /// - /// This function also draw selection-tick(s) if IsSelectionRangeEnabled is 'true' and - /// SelectionStart and SelectionEnd are valid. - /// - /// The primary ticks (for Mininum and Maximum value) height will be 100% of TickBar's render size (use Width or Height - /// depends on Placement property). - /// - /// The secondary ticks (all other ticks, including selection-tics) height will be 75% of TickBar's render size. - /// - /// Brush that use to fill ticks is specified by Fill property. - /// - public override void Render(DrawingContext dc) - { - var size = new Size(Bounds.Width, Bounds.Height); - var range = Maximum - Minimum; - var logicalToPhysical = 1.0; - var startPoint = new Point(); - var endPoint = new Point(); - var rSpace = Orientation == Orientation.Horizontal ? ReservedSpace.Width : ReservedSpace.Height; - var s = (double)GetValue(SliderAssist.SizeTickProperty); - - // Take Thumb size in to account - double halfReservedSpace = rSpace * 0.5; - - var pen = new Pen(Fill, (double)GetValue(SliderAssist.ThicknessTickProperty)); - - void DrawTick(double x, double y, Pen pen, double size,double scaleX = 0.5,double scaleY = 0.5) - { - double halfS = pen.Thickness * size * 0.5; - double dx = x * scaleX - halfS; - double dy = y * scaleY - halfS; - dc.DrawGeometry(pen.Brush, pen, new EllipseGeometry( - new Rect(dx, dy, size, size))); - } - - // Reduce tick interval if it is more than would be visible on the screen - double interval = TickFrequency; - - // This property is rarely set so let's try to avoid the GetValue - // caching of the mutable default value - var ticks = Ticks ?? null; - - switch (Orientation) - { - case Orientation.Horizontal: - if (MathUtilities.GreaterThanOrClose(rSpace, size.Width)) - { - return; - } - size = new Size(size.Width - rSpace, size.Height); - startPoint = new Point(halfReservedSpace, size.Height); - endPoint = new Point(halfReservedSpace + size.Width, size.Height); - logicalToPhysical = size.Width / range; - - if (interval > 0.0) - { - double minInterval = (Maximum - Minimum) / size.Width; - if (interval < minInterval) - { - interval = minInterval; - } - } - - // Draw Min & Max tick - DrawTick(startPoint.X, startPoint.Y, pen, s, scaleX: 1); - DrawTick(endPoint.X, startPoint.Y, pen, s, scaleX: 1); - - // Draw ticks using specified Ticks collection - if (ticks?.Count > 0) - { - for (int i = 0; i < ticks.Count; i++) - { - double x = logicalToPhysical + startPoint.X; - DrawTick(x, startPoint.Y, pen, s, scaleX: 1); - } - } - // Draw ticks using specified TickFrequency - else if (interval > 0.0) - { - for (double i = interval; i < range; i += interval) - { - double x = i * logicalToPhysical + startPoint.X; - DrawTick(x, startPoint.Y, pen, s, scaleX: 1); - } - } - break; - - case Orientation.Vertical: - if (MathUtilities.GreaterThanOrClose(rSpace, size.Height)) - { - return; - } - size = new Size(size.Width, size.Height - rSpace); - startPoint = new Point(size.Width, size.Height + halfReservedSpace); - endPoint = new Point(size.Width, halfReservedSpace); - logicalToPhysical = size.Height / range * -1; - - if (interval > 0.0) - { - double minInterval = (Maximum - Minimum) / size.Height; - if (interval < minInterval) - { - interval = minInterval; - } - } - - // Draw Min & Max tick - DrawTick(startPoint.X, startPoint.Y, pen, s, scaleY: 1); - DrawTick(startPoint.X, endPoint.Y, pen, s, scaleY: 1); - - // Draw ticks using specified Ticks collection - if (ticks?.Count > 0) - { - for (int i = 0; i < ticks.Count; i++) - { - double y = logicalToPhysical + startPoint.Y; - DrawTick(startPoint.X, y, pen, s, scaleY: 1); - } - } - // Draw ticks using specified TickFrequency - else if (interval > 0.0) - { - for (double i = interval; i < range; i += interval) - { - double y = i * logicalToPhysical + startPoint.Y; - DrawTick(startPoint.X, y, pen, s, scaleY: 1); - } - } - break; - } - } - } -} From 6e83275dd24749a53bf0d9b991c48453dd8b70b3 Mon Sep 17 00:00:00 2001 From: appleneko2001 Date: Fri, 3 Jun 2022 05:16:35 +0900 Subject: [PATCH 30/92] Ignoring MaterialCalendar.cs (because its empty. Not ready yet.) --- Material.Styles/Material.Styles.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Material.Styles/Material.Styles.csproj b/Material.Styles/Material.Styles.csproj index 8685dd2..05d3d82 100644 --- a/Material.Styles/Material.Styles.csproj +++ b/Material.Styles/Material.Styles.csproj @@ -59,4 +59,8 @@ + + + + From d94d21786f71e16479750f773516eba46fbfe95f Mon Sep 17 00:00:00 2001 From: appleneko2001 Date: Fri, 3 Jun 2022 05:18:16 +0900 Subject: [PATCH 31/92] Update FloatingButtonBuilder.cs --- .../Builders/FloatingButtonBuilder.cs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Material.Styles/Builders/FloatingButtonBuilder.cs b/Material.Styles/Builders/FloatingButtonBuilder.cs index 9e86224..0c81c39 100644 --- a/Material.Styles/Builders/FloatingButtonBuilder.cs +++ b/Material.Styles/Builders/FloatingButtonBuilder.cs @@ -1,27 +1,28 @@ using Avalonia.Controls; using Avalonia.Layout; using Avalonia.Media; +using Material.Styles.Controls; namespace Material.Styles.Builders { public class FloatingButtonBuilder { - private double spacing = 12.0; - private Control? icon; - private Control? text; + private double _spacing = 12.0; + private Control? _icon; + private Control? _text; /// /// Set spacing between icon and text views. /// public FloatingButtonBuilder SetSpacing(double spacing = 12.0) { - this.spacing = spacing; + _spacing = spacing; return this; } public FloatingButtonBuilder SetIcon(Control view) { - icon = view; + _icon = view; return this; } @@ -46,7 +47,7 @@ namespace Material.Styles.Builders public FloatingButtonBuilder SetText(Control view) { - text = view; + _text = view; return this; } @@ -58,19 +59,19 @@ namespace Material.Styles.Builders { Panel panel; - if (icon != null) + if (_icon != null) { panel = new StackPanel { Orientation = Orientation.Horizontal }; - panel.Children.Add(icon); + panel.Children.Add(_icon); var separator = new Separator { Name = "PART_Spacing", - Width = spacing, + Width = _spacing, Background = Brushes.Transparent, Foreground = Brushes.Transparent }; @@ -82,8 +83,8 @@ namespace Material.Styles.Builders panel = new Panel(); } - if(text != null) - panel.Children.Add(text); + if(_text != null) + panel.Children.Add(_text); var button = new FloatingButton { From d9b1d8533093e95d8fde6276e209f77d2700fb1c Mon Sep 17 00:00:00 2001 From: appleneko2001 Date: Fri, 3 Jun 2022 05:18:45 +0900 Subject: [PATCH 32/92] Move MaterialUnderline.xaml.cs to Controls folder --- Material.Styles/MaterialUnderline.xaml.cs | 59 ----------------------- 1 file changed, 59 deletions(-) delete mode 100644 Material.Styles/MaterialUnderline.xaml.cs diff --git a/Material.Styles/MaterialUnderline.xaml.cs b/Material.Styles/MaterialUnderline.xaml.cs deleted file mode 100644 index 3b5ba31..0000000 --- a/Material.Styles/MaterialUnderline.xaml.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Threading; -using Avalonia; -using Avalonia.Controls; -using Avalonia.Controls.Shapes; -using Avalonia.Media; -using Avalonia.Threading; - -namespace Material.Styles { - public class MaterialUnderline : ContentControl { - /// - /// Defines the property. - /// - public static readonly StyledProperty BackgroundColorProperty = - AvaloniaProperty.Register(nameof(BackgroundColor)); - - public IBrush BackgroundColor - { - get => GetValue(BackgroundColorProperty); - set => SetValue(BackgroundColorProperty, value); - } - - /// - /// Defines the property. - /// - public static readonly StyledProperty ActiveColorProperty = - AvaloniaProperty.Register(nameof(ActiveColor)); - - public IBrush ActiveColor - { - get => GetValue(BackgroundColorProperty); - set => SetValue(BackgroundColorProperty, value); - } - - /// - /// Defines the property. - /// - public static readonly StyledProperty IsActiveProperty = - AvaloniaProperty.Register(nameof(IsActive)); - - public bool IsActive - { - get => GetValue(IsActiveProperty); - set => SetValue(IsActiveProperty, value); - } - - /// - /// Defines the property. - /// - public static readonly StyledProperty IsExpandedProperty = - AvaloniaProperty.Register(nameof(IsExpanded)); - - public bool IsExpanded - { - get => GetValue(IsExpandedProperty); - set => SetValue(IsExpandedProperty, value); - } - } -} \ No newline at end of file From 38a10d09481d7a162e42e926a0f6eba62fc4d024 Mon Sep 17 00:00:00 2001 From: appleneko2001 Date: Fri, 3 Jun 2022 05:23:39 +0900 Subject: [PATCH 33/92] Redesign most styles (incomplete, change classes by following css classes naming standard, use static part naming in most template parts, improve most controls looks and etc.) --- Material.Styles/Button.xaml | 526 +++++---- Material.Styles/ButtonSpinner.xaml | 5 +- Material.Styles/Calendar.xaml | 28 +- Material.Styles/CalendarDayButton.xaml | 81 +- Material.Styles/CalendarItem.xaml | 176 ++- Material.Styles/Card.xaml | 20 +- Material.Styles/CircleClockPicker.axaml | 52 + Material.Styles/CircleClockPickerCell.axaml | 47 + Material.Styles/ColorZone.xaml | 71 +- Material.Styles/ComboBox.xaml | 895 +++++++------- Material.Styles/ContextMenu.xaml | 48 +- Material.Styles/DataValidationErrors.xaml | 88 +- Material.Styles/DatePicker.xaml | 149 +-- Material.Styles/DialogHost.axaml | 5 +- Material.Styles/Expander.xaml | 23 +- Material.Styles/FloatingButton.xaml | 49 +- Material.Styles/FlyoutPresenter.axaml | 7 +- Material.Styles/ListBoxItem.xaml | 4 +- Material.Styles/MaterialToolKit.xaml | 38 +- Material.Styles/MaterialUnderline.xaml | 116 +- Material.Styles/MenuItem.xaml | 11 +- Material.Styles/NavigationDrawer.xaml | 210 ++-- Material.Styles/NumericUpDown.xaml | 23 +- Material.Styles/OverlayPopupHost.xaml | 33 +- Material.Styles/PopupRoot.xaml | 13 +- Material.Styles/ProgressBar.xaml | 94 +- Material.Styles/RepeatButton.xaml | 4 +- Material.Styles/ScrollBar.xaml | 11 +- Material.Styles/SideSheet.xaml | 58 +- Material.Styles/Slider.xaml | 1158 +++++++++++-------- Material.Styles/SnackbarHost.xaml | 13 +- Material.Styles/TextBlock.xaml | 146 +-- Material.Styles/TextBox.xaml | 1071 +++++++++-------- Material.Styles/ToggleButton.xaml | 254 ++-- Material.Styles/ToggleSwitch.xaml | 6 +- Material.Styles/TreeView.xaml | 174 ++- 36 files changed, 3156 insertions(+), 2551 deletions(-) create mode 100644 Material.Styles/CircleClockPicker.axaml create mode 100644 Material.Styles/CircleClockPickerCell.axaml diff --git a/Material.Styles/Button.xaml b/Material.Styles/Button.xaml index 5e9e228..f425260 100644 --- a/Material.Styles/Button.xaml +++ b/Material.Styles/Button.xaml @@ -1,246 +1,302 @@ - - - 0.12 - 0.38 - 0.26 - + + + 0.12 + 0.38 + 0.26 + - - - - - - - - - - - + - - - - - + - + - + + + + + + + + + + - - - - - - - - - - - + - + + + + + + - - - - - - - - - + - - - + - + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Material.Styles/ButtonSpinner.xaml b/Material.Styles/ButtonSpinner.xaml index b4030f6..29248d0 100644 --- a/Material.Styles/ButtonSpinner.xaml +++ b/Material.Styles/ButtonSpinner.xaml @@ -1,11 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/Material.Styles/CalendarDayButton.xaml b/Material.Styles/CalendarDayButton.xaml index 1099ed5..934aaf2 100644 --- a/Material.Styles/CalendarDayButton.xaml +++ b/Material.Styles/CalendarDayButton.xaml @@ -5,26 +5,51 @@ // All other rights reserved. --> - + - - - - - - - + + - - - - + + \ No newline at end of file diff --git a/Material.Styles/CalendarItem.xaml b/Material.Styles/CalendarItem.xaml index 839e1cd..cc4f481 100644 --- a/Material.Styles/CalendarItem.xaml +++ b/Material.Styles/CalendarItem.xaml @@ -6,96 +6,88 @@ --> - - - + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:controls="clr-namespace:Material.Styles.Controls"> + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Material.Styles/Card.xaml b/Material.Styles/Card.xaml index 7965993..0e2d16b 100644 --- a/Material.Styles/Card.xaml +++ b/Material.Styles/Card.xaml @@ -1,8 +1,10 @@ - + + + + + + + diff --git a/Material.Styles/CircleClockPickerCell.axaml b/Material.Styles/CircleClockPickerCell.axaml new file mode 100644 index 0000000..0954c45 --- /dev/null +++ b/Material.Styles/CircleClockPickerCell.axaml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + diff --git a/Material.Styles/ColorZone.xaml b/Material.Styles/ColorZone.xaml index 5b36298..0917766 100644 --- a/Material.Styles/ColorZone.xaml +++ b/Material.Styles/ColorZone.xaml @@ -2,8 +2,9 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:assists="clr-namespace:Material.Styles.Assists" - xmlns:cc="clr-namespace:Material.Styles"> - - - - - - - - - - - - - - - - - - - - - - - - - + - + \ No newline at end of file diff --git a/Material.Styles/ComboBox.xaml b/Material.Styles/ComboBox.xaml index 4b6148b..a95c6b4 100644 --- a/Material.Styles/ComboBox.xaml +++ b/Material.Styles/ComboBox.xaml @@ -2,409 +2,520 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:cc="clr-namespace:Material.Styles" xmlns:assists="clr-namespace:Material.Styles.Assists" - xmlns:styles="clr-namespace:Material.Styles"> - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Material.Styles/ContextMenu.xaml b/Material.Styles/ContextMenu.xaml index e189cee..956483c 100644 --- a/Material.Styles/ContextMenu.xaml +++ b/Material.Styles/ContextMenu.xaml @@ -1,39 +1,51 @@ + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:controls="clr-namespace:Material.Styles.Controls" + xmlns:naming="clr-namespace:Material.Styles.Resources.Naming" + xmlns:assists="clr-namespace:Material.Styles.Assists" > - - + + + + \ No newline at end of file diff --git a/Material.Styles/DataValidationErrors.xaml b/Material.Styles/DataValidationErrors.xaml index 709eaa1..de15d77 100644 --- a/Material.Styles/DataValidationErrors.xaml +++ b/Material.Styles/DataValidationErrors.xaml @@ -1,49 +1,45 @@ - - - - - - - - - \ No newline at end of file + + + + + diff --git a/Material.Styles/DatePicker.xaml b/Material.Styles/DatePicker.xaml index 11b7ffc..90d37cf 100644 --- a/Material.Styles/DatePicker.xaml +++ b/Material.Styles/DatePicker.xaml @@ -3,73 +3,92 @@ xmlns:assists="clr-namespace:Material.Styles.Assists" xmlns:converters="clr-namespace:Material.Styles.Converters" xmlns:styles="clr-namespace:Material.Styles" - xmlns:controls="clr-namespace:Material.Styles.Controls"> - - - - - - - - + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Material.Styles/DialogHost.axaml b/Material.Styles/DialogHost.axaml index 4e5cb72..27915f9 100644 --- a/Material.Styles/DialogHost.axaml +++ b/Material.Styles/DialogHost.axaml @@ -2,11 +2,12 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:dialogHost="clr-namespace:DialogHost;assembly=DialogHost.Avalonia" xmlns:styles="clr-namespace:Material.Styles" - xmlns:assists="clr-namespace:Material.Styles.Assists"> + xmlns:assists="clr-namespace:Material.Styles.Assists" + xmlns:controls="clr-namespace:Material.Styles.Controls"> @@ -122,14 +123,14 @@ - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/Material.Styles/FlyoutPresenter.axaml b/Material.Styles/FlyoutPresenter.axaml index 6122432..c59358a 100644 --- a/Material.Styles/FlyoutPresenter.axaml +++ b/Material.Styles/FlyoutPresenter.axaml @@ -1,6 +1,7 @@ + xmlns:styles="clr-namespace:Material.Styles" + xmlns:controls="clr-namespace:Material.Styles.Controls"> 456 758 @@ -20,7 +21,7 @@ - @@ -36,7 +37,7 @@ HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" /> - + diff --git a/Material.Styles/ListBoxItem.xaml b/Material.Styles/ListBoxItem.xaml index c37d5d8..711bebf 100644 --- a/Material.Styles/ListBoxItem.xaml +++ b/Material.Styles/ListBoxItem.xaml @@ -49,8 +49,8 @@ - + xmlns:naming="clr-namespace:Material.Styles.Resources.Naming" + xmlns:controls="clr-namespace:Material.Styles.Controls"> + + + + + + + + - + - - - - + + + + + + + + \ No newline at end of file diff --git a/Material.Styles/MenuItem.xaml b/Material.Styles/MenuItem.xaml index 4868ab3..2376b72 100644 --- a/Material.Styles/MenuItem.xaml +++ b/Material.Styles/MenuItem.xaml @@ -3,7 +3,8 @@ xmlns:conv="clr-namespace:Avalonia.Controls.Converters;assembly=Avalonia.Controls" xmlns:sys="clr-namespace:System;assembly=netstandard" xmlns:styles="clr-namespace:Material.Styles;assembly=Material.Styles" - xmlns:ripple="clr-namespace:Material.Ripple;assembly=Material.Ripple"> + xmlns:ripple="clr-namespace:Material.Ripple;assembly=Material.Ripple" + xmlns:controls="clr-namespace:Material.Styles.Controls"> @@ -159,7 +160,7 @@ PlacementMode="Right" IsLightDismissEnabled="False" IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}"> - + @@ -170,7 +171,7 @@ Margin="0 8" /> - + @@ -229,7 +230,7 @@ IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}" IsLightDismissEnabled="False" WindowManagerAddShadowHint="False"> - + @@ -240,7 +241,7 @@ Margin="0,8" /> - + diff --git a/Material.Styles/NavigationDrawer.xaml b/Material.Styles/NavigationDrawer.xaml index 580a708..82e7ff3 100644 --- a/Material.Styles/NavigationDrawer.xaml +++ b/Material.Styles/NavigationDrawer.xaml @@ -3,80 +3,80 @@ xmlns:cc="clr-namespace:Material.Styles" xmlns:converters="clr-namespace:Material.Styles.Converters" xmlns:parameters="clr-namespace:Material.Styles.Converters.Parameters" - xmlns:assists="clr-namespace:Material.Styles.Assists"> + xmlns:assists="clr-namespace:Material.Styles.Assists" + xmlns:naming="clr-namespace:Material.Styles.Resources.Naming" + xmlns:controls="clr-namespace:Material.Styles.Controls"> - - - - - + + + + + - - - - - - - - - - + + + + + + + + - - - - - + + + + + + + - + - - - - - - - - - - + + + + + + - - + + + + - - - - - - + + + - - + + + - + @@ -91,5 +86,34 @@ - + + + + + + + + + + + - + - + CornerRadius="32" Margin="8"> - @@ -341,5 +345,7 @@ + + diff --git a/Material.Demo/Pages/Home.axaml b/Material.Demo/Pages/Home.axaml index ef8c194..4f2b4b1 100644 --- a/Material.Demo/Pages/Home.axaml +++ b/Material.Demo/Pages/Home.axaml @@ -1,84 +1,82 @@  - - - + x:Class="Material.Demo.Pages.Home"> + - - - - - - - - - - - - + - - - + - + @@ -113,7 +114,7 @@ - + - + \ No newline at end of file diff --git a/Material.Demo/Pages/ListsDemo.axaml b/Material.Demo/Pages/ListsDemo.axaml index 7be94be..7e9d7a7 100644 --- a/Material.Demo/Pages/ListsDemo.axaml +++ b/Material.Demo/Pages/ListsDemo.axaml @@ -5,6 +5,7 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" xmlns:styles="clr-namespace:Material.Styles;assembly=Material.Styles" xmlns:showMeTheXaml="clr-namespace:ShowMeTheXaml;assembly=ShowMeTheXaml.Avalonia" + xmlns:controls="clr-namespace:Material.Styles.Controls;assembly=Material.Styles" x:Class="Material.Demo.Pages.ListsDemo"> @@ -16,14 +17,14 @@ - + - + diff --git a/Material.Demo/Pages/PickersDemo.axaml b/Material.Demo/Pages/PickersDemo.axaml index 9d11ad1..1a8b99d 100644 --- a/Material.Demo/Pages/PickersDemo.axaml +++ b/Material.Demo/Pages/PickersDemo.axaml @@ -37,6 +37,14 @@ assists:DatePickerAssist.DateTimeFormat="\M\y \for\ma\t MMM dd, yyyy"> + + + + + + + + diff --git a/Material.Demo/Pages/ProgressIndicatorDemo.axaml b/Material.Demo/Pages/ProgressIndicatorDemo.axaml index fb3eead..ea30841 100644 --- a/Material.Demo/Pages/ProgressIndicatorDemo.axaml +++ b/Material.Demo/Pages/ProgressIndicatorDemo.axaml @@ -5,6 +5,7 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" xmlns:styles="clr-namespace:Material.Styles;assembly=Material.Styles" xmlns:showMeTheXaml="clr-namespace:ShowMeTheXaml;assembly=ShowMeTheXaml.Avalonia" + xmlns:controls="clr-namespace:Material.Styles.Controls;assembly=Material.Styles" x:Class="Material.Demo.Pages.ProgressIndicatorDemo"> @@ -29,39 +30,39 @@ - + - + - - - + + - - - + + - - - + + - - - + + diff --git a/Material.Demo/Pages/ScrollViewerDemo.axaml b/Material.Demo/Pages/ScrollViewerDemo.axaml index 8e992c2..103d69f 100644 --- a/Material.Demo/Pages/ScrollViewerDemo.axaml +++ b/Material.Demo/Pages/ScrollViewerDemo.axaml @@ -6,10 +6,11 @@ xmlns:styles="clr-namespace:Material.Styles;assembly=Material.Styles" xmlns:pages="clr-namespace:Material.Demo.Pages" xmlns:showMeTheXaml="clr-namespace:ShowMeTheXaml;assembly=ShowMeTheXaml.Avalonia" + xmlns:controls="clr-namespace:Material.Styles.Controls;assembly=Material.Styles" x:Class="Material.Demo.Pages.ScrollViewerDemo"> - @@ -19,13 +20,13 @@ - + - + @@ -33,24 +34,24 @@ - + - + - + - + diff --git a/Material.Demo/Pages/SideSheetDemo.axaml b/Material.Demo/Pages/SideSheetDemo.axaml index ec2adde..fcb13a9 100644 --- a/Material.Demo/Pages/SideSheetDemo.axaml +++ b/Material.Demo/Pages/SideSheetDemo.axaml @@ -6,6 +6,7 @@ xmlns:pages="clr-namespace:Material.Demo.Pages" xmlns:models="clr-namespace:Material.Demo.Models" xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" + xmlns:controls="clr-namespace:Material.Styles.Controls;assembly=Material.Styles" mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450" x:Class="Material.Demo.Pages.SideSheetDemo" x:DataType="models:SideSheetDemoViewModel"> @@ -16,21 +17,21 @@ - - + - +