diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml index 65e242e..c9060ef 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/dotnetcore.yml @@ -11,6 +11,7 @@ on: jobs: build: runs-on: ubuntu-latest + name: Build solution steps: - uses: actions/checkout@v1 - name: Setup .NET Core diff --git a/.github/workflows/publish_nightly.yml b/.github/workflows/publish_nightly.yml index 716b461..7a20455 100644 --- a/.github/workflows/publish_nightly.yml +++ b/.github/workflows/publish_nightly.yml @@ -6,7 +6,9 @@ on: jobs: build: + name: Build and pack runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, 'no nightly')" steps: - uses: actions/checkout@v2 - name: Setup .NET @@ -31,6 +33,7 @@ jobs: name: artifacts path: artifacts/ numerge: + name: Merge packages and publish nightly runs-on: ubuntu-latest needs: [build] steps: diff --git a/.github/workflows/publish_release.yml b/.github/workflows/publish_release.yml index f318eab..4090621 100644 --- a/.github/workflows/publish_release.yml +++ b/.github/workflows/publish_release.yml @@ -7,6 +7,7 @@ on: jobs: build: runs-on: ubuntu-latest + name: Build and pack steps: - uses: actions/checkout@v2 - name: Setup .NET @@ -32,6 +33,7 @@ jobs: path: artifacts/ numerge: runs-on: ubuntu-latest + name: Merge packages and publish release needs: [build] steps: - name: Checkout Numerge @@ -56,4 +58,4 @@ jobs: - name: Publish to Nuget run: dotnet nuget push "artifacts/output/Material.Avalonia.*.nupkg" --api-key ${{secrets.NUGET_KEY}} --source https://api.nuget.org/v3/index.json --skip-duplicate - name: Publish to GitHub Packages - run: dotnet nuget push "artifacts/output/Material.Avalonia.*.nupkg" --api-key ${{secrets.GITHUB_TOKEN}} --source https://nuget.pkg.github.com/AvaloniaCommunity/index.json --skip-duplicate \ No newline at end of file + run: dotnet nuget push "artifacts/output/Material.Avalonia.*.nupkg" --api-key ${{secrets.GITHUB_TOKEN}} --source https://nuget.pkg.github.com/AvaloniaCommunity/index.json --skip-duplicate diff --git a/Banner.svg b/Banner.svg deleted file mode 100644 index 522a857..0000000 --- a/Banner.svg +++ /dev/null @@ -1,453 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/LICENSE b/LICENSE index c21258c..7b27ad7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 CreateLab +Copyright (c) CreateLab, SKProCH, appleneko2001 and Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Material.Avalonia/Material.Avalonia.Templates.xaml.cs b/Material.Avalonia/Material.Avalonia.Templates.xaml.cs index 99ce482..05061de 100644 --- a/Material.Avalonia/Material.Avalonia.Templates.xaml.cs +++ b/Material.Avalonia/Material.Avalonia.Templates.xaml.cs @@ -1,3 +1,6 @@ -namespace Material.Avalonia { - public class MaterialAvaloniaTemplates : global::Avalonia.Styling.Styles { } +namespace Material.Avalonia +{ + public class MaterialAvaloniaTemplates : global::Avalonia.Styling.Styles + { + } } \ No newline at end of file diff --git a/Material.Avalonia/Material.Avalonia.csproj b/Material.Avalonia/Material.Avalonia.csproj index 17b5dc6..4cc58a5 100644 --- a/Material.Avalonia/Material.Avalonia.csproj +++ b/Material.Avalonia/Material.Avalonia.csproj @@ -2,36 +2,45 @@ netstandard2.0 - 3.0.0-avalonia10-preview1 Material.Avalonia Larymar,SKProCH,Appleneko2001 This repository is a set of styles that will help you customize your application in an appropriate material design. https://github.com/AvaloniaCommunity/Material.Avalonia - https://raw.githubusercontent.com/AvaloniaCommunity/Material.Avalonia/master/LICENSE https://github.com/AvaloniaCommunity/Material.Avalonia git 8 + avalonia xaml material design theme colour color ui ux material-design google-material + MIT + README.md + FavIcon.png + 3.0.0-avalonia10-preview2 -This is the same as 3.0.0-rc0 +Most likely latest version with Avalonia 0.10.x support Version 3.0.0-rc0 was replaced with 3.0.0-avalonia10-preview1 (in order to keep version names clear) - -- Removed Material.Icons dependency (must be installed separately from https://github.com/AvaloniaUtils/Material.Icons.Avalonia) #127 -- Rework styles (details https://github.com/AvaloniaCommunity/Material.Avalonia/wiki/Advanced-Theming#migrating-25---30) -- Fix neverest version of Avalonia support #133 -- Add SideSheet control #145 -- Add opening navigation drawer from right side #131 -- Fix context menu item submenu appearing and disappearing in infinite loop #142 -- Fix binding on LeftDrawerWidth hides all content/behavior of NavigationDrawer #151 -- Fix custom dialog view datacontext binding #152 -- Fix ProgressBar styles inheritance #125 -- Fix show password button is not visible until clicked #124 -- Fix CustomDialogs content #121 -- Set KeyboardNavigation.TabNavigation property to Continue on ItemsPresenter #146 -- Stop Spinner from rotating when hidden. -- Other fixes and improvements +- Bump Avalonia version dependency to 0.10.13 +- Implement adapting to system base theme mode on Windows +- Improve/redesign Expander, TextBlock, Separator and other styles +- Create ContentExpandControl, used for animate resizing +- Add TransitioningContentControl style support +- Allow use Avalonia.Diagnostics to dialogs when using DEBUG profile +- Attach TextBlock properties to NumericUpDown +- Add padding to alert dialog and text field dialog +- Fix AutoCorrectPositionConverter #172 +- Add default value for Slider, correct dots positioning +- Change resources apply logic to avoid colors apply delay while application starting +- Correct HsbConverter.ToColor method +- Add static part names pool +- Add auto-expand feature on NavigationDrawer +- Update contrast color algorithm +- Add InputMethod.IsInputMethodEnabled for TextBox + + + + + $(VersionPrefix).$(PatchNumber) @@ -44,7 +53,7 @@ Version 3.0.0-rc0 was replaced with 3.0.0-avalonia10-preview1 (in order to keep - + diff --git a/Material.Colors/ColorManipulation/ColorHelper.cs b/Material.Colors/ColorManipulation/ColorHelper.cs index 7b1e91c..1d09e98 100644 --- a/Material.Colors/ColorManipulation/ColorHelper.cs +++ b/Material.Colors/ColorManipulation/ColorHelper.cs @@ -1,51 +1,131 @@ using System; using Avalonia.Media; -namespace Material.Colors.ColorManipulation { - public static class ColorHelper { - public static Color ContrastingForegroundColor(this Color color) { - double RgbSrgb(double d) { - d = d / 255.0; - return d > 0.03928 - ? d = Math.Pow((d + 0.055) / 1.055, 2.4) - : d = d / 12.92; - } +namespace Material.Colors.ColorManipulation +{ + public static class ColorHelper + { + [Obsolete("Please use PickContrastColor method instead.")] + public static Color ContrastingForegroundColor(this Color color) => + PickContrastColor(color); - var r = RgbSrgb(color.R); - var g = RgbSrgb(color.G); - var b = RgbSrgb(color.B); - - var luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b; - return luminance > 0.179 ? Avalonia.Media.Colors.Black : Avalonia.Media.Colors.White; + /// + /// Choose most accessible color by algorithm. The primary and secondary color is pure black and white. + /// + /// Background color + /// Minimal contrast ratio. It is 4.5 by default. + /// The most accessible color (AAA or AA level) for text + public static Color PickContrastColor(this Color color, double ratio = 4.5) + { + return AlgorithmContrastColor(color, Avalonia.Media.Colors.Black, Avalonia.Media.Colors.White, ratio); } - public static Color ShiftLightness(this Color color, int amount = 1) { + /// + /// Choose most accessible color by algorithm. + /// + /// Background color + /// Primary accessible color + /// Secondary accessible color + /// Minimal contrast ratio. It is 4.5 by default. + /// The most accessible color for text or control (not guarantee its accessible because the primary and secondary colors might not most used on UIs.) + public static Color PickContrastColor(this Color color, Color a, Color b, double ratio = 4.5) + { + return AlgorithmContrastColor(color, a, b, ratio); + } + + public static Color ShiftLightness(this Color color, int amount = 1) + { var lab = color.ToLab(); var shifted = new Lab(lab.L - LabConstants.Kn * amount, lab.A, lab.B); return shifted.ToColor(); } - public static Color Darken(this Color color, int amount = 1) { + public static Color Darken(this Color color, int amount = 1) + { return color.ShiftLightness(amount); } - public static Color Lighten(this Color color, int amount = 1) { + public static Color Lighten(this Color color, int amount = 1) + { return color.ShiftLightness(-amount); } + /// + /// Calculate relative luminance of color. + /// https://www.w3.org/TR/WCAG21/#dfn-relative-luminance + /// + /// Color used for measurement. + /// The magnitude of relative luminance of color + public static double RelativeLuminance(this Color c) + { + double Process(double s) => + s < 0.03928 ? s / 12.92 : Math.Pow((s + 0.055) / 1.055, 2.4); + + double dR = (double) c.R / 255, + dG = (double) c.G / 255, + dB = (double) c.B / 255; + + var r = Process(dR); + var g = Process(dG); + var b = Process(dB); + + return 0.2126 * r + 0.7152 * g + 0.0722 * b; + } + + /// + /// Get color contrast between two colors + /// + /// First color + /// Second color + /// Maximum possible contrast value. E.g: contrast white and black is 21, then it return 21. + public static double Contrast(this Color a, Color b) + { + var l1 = RelativeLuminance(a) + 0.05; + var l2 = RelativeLuminance(b) + 0.05; + + var ratio = l1 / l2; + + if (l2 > l1) + ratio = l2 / l1; + + return ratio; + } + /// /// Calculates the CIE76 distance between two colors. /// /// /// /// - public static double Difference(this Color color, Color other) { + public static double Difference(this Color color, Color other) + { var lab1 = color.ToLab(); - var lab2 = color.ToLab(); + var lab2 = other.ToLab(); return Math.Sqrt(Math.Pow(lab2.L - lab1.L, 2) + Math.Pow(lab2.A - lab1.A, 2) + Math.Pow(lab2.B - lab1.B, 2)); } + + // https://github.com/LeaVerou/contrast-ratio + private static Color AlgorithmContrastColor(Color backColor, Color a, Color b, double ratio = 4.5) + { + var contrast1 = Contrast(backColor, a); + var contrast2 = Contrast(backColor, b); + + Color result; + + switch (contrast1 >= ratio) + { + case true: + result = contrast1 > contrast2 ? a : b; + break; + default: + result = contrast2 > contrast1 ? b : a; + break; + } + + return result; + } } } \ No newline at end of file diff --git a/Material.Colors/ColorManipulation/Hsb.cs b/Material.Colors/ColorManipulation/Hsb.cs index 9c379fc..9c5a1fa 100644 --- a/Material.Colors/ColorManipulation/Hsb.cs +++ b/Material.Colors/ColorManipulation/Hsb.cs @@ -1,10 +1,13 @@ -namespace Material.Colors.ColorManipulation { - public struct Hsb { +namespace Material.Colors.ColorManipulation +{ + public struct Hsb + { public double Hue { get; } public double Saturation { get; } public double Brightness { get; } - public Hsb(double hue, double saturation, double brightness) { + public Hsb(double hue, double saturation, double brightness) + { Hue = hue; Saturation = saturation; Brightness = brightness; diff --git a/Material.Colors/ColorManipulation/HsbConverter.cs b/Material.Colors/ColorManipulation/HsbConverter.cs index df3caa6..dea5ae2 100644 --- a/Material.Colors/ColorManipulation/HsbConverter.cs +++ b/Material.Colors/ColorManipulation/HsbConverter.cs @@ -2,9 +2,12 @@ using System.Linq; using Avalonia.Media; -namespace Material.Colors.ColorManipulation { - public static class HsbConverter { - public static Color ToColor(this Hsb hsv) { +namespace Material.Colors.ColorManipulation +{ + public static class HsbConverter + { + public static Color ToColor(this Hsb hsv) + { var h = hsv.Hue; var s = hsv.Saturation; var b = hsv.Brightness; @@ -25,17 +28,27 @@ namespace Material.Colors.ColorManipulation { var q = b * (1 - s * f); var t = b * (1 - s * (1 - f)); - if (i == 0) return Color.FromRgb((byte) b, (byte) t, (byte) p); - if (i == 1) return Color.FromRgb((byte) q, (byte) b, (byte) p); - if (i == 2) return Color.FromRgb((byte) p, (byte) b, (byte) t); - if (i == 3) return Color.FromRgb((byte) p, (byte) q, (byte) b); - if (i == 4) return Color.FromRgb((byte) t, (byte) p, (byte) b); - if (i == 5) return Color.FromRgb((byte) b, (byte) p, (byte) q); - - throw new Exception("Invalid HSB values"); + switch (i) + { + case 0: + return Color.FromRgb((byte) b, (byte) t, (byte) p); + case 1: + return Color.FromRgb((byte) q, (byte) b, (byte) p); + case 2: + return Color.FromRgb((byte) p, (byte) b, (byte) t); + case 3: + return Color.FromRgb((byte) p, (byte) q, (byte) b); + case 4: + return Color.FromRgb((byte) t, (byte) p, (byte) b); + case 5: + return Color.FromRgb((byte) b, (byte) p, (byte) q); + default: + throw new Exception("Invalid HSB values"); + } } - public static Hsb ToHsb(this Color color) { + public static Hsb ToHsb(this Color color) + { double r = color.R; double g = color.G; double b = color.B; @@ -53,10 +66,12 @@ namespace Material.Colors.ColorManipulation { var d = max - min; var s = max.IsCloseTo(0) ? 0 : d / max; - if (max.IsCloseTo(min)) { + if (max.IsCloseTo(min)) + { h = 0; // achromatic } - else { + else + { if (max.IsCloseTo(r)) h = (g - b) / d + (g < b ? 6 : 0); else if (max.IsCloseTo(g)) @@ -69,7 +84,8 @@ namespace Material.Colors.ColorManipulation { return new Hsb(h, s, v); } - private static bool IsCloseTo(this double value, double target, double tolerance = double.Epsilon) { + private static bool IsCloseTo(this double value, double target, double tolerance = double.Epsilon) + { return Math.Abs(value - target) < tolerance; } } diff --git a/Material.Colors/ColorManipulation/Hsl.cs b/Material.Colors/ColorManipulation/Hsl.cs index a04a61e..6bc3c1b 100644 --- a/Material.Colors/ColorManipulation/Hsl.cs +++ b/Material.Colors/ColorManipulation/Hsl.cs @@ -1,10 +1,13 @@ -namespace Material.Colors.ColorManipulation { - internal struct Hsl { +namespace Material.Colors.ColorManipulation +{ + internal struct Hsl + { public double H { get; } public double S { get; } public double L { get; } - public Hsl(double h, double s, double l) { + public Hsl(double h, double s, double l) + { H = h; S = s; L = l; diff --git a/Material.Colors/ColorManipulation/HslConverter.cs b/Material.Colors/ColorManipulation/HslConverter.cs index 1321302..c5bcd22 100644 --- a/Material.Colors/ColorManipulation/HslConverter.cs +++ b/Material.Colors/ColorManipulation/HslConverter.cs @@ -1,10 +1,14 @@ using System; using Avalonia.Media; -namespace Material.Colors.ColorManipulation { - internal static class HslConverter { - public static Color ToColor(this Hsl hsl) { - double HsvRbg(double v1, double v2, double vH) { +namespace Material.Colors.ColorManipulation +{ + internal static class HslConverter + { + public static Color ToColor(this Hsl hsl) + { + double HsvRbg(double v1, double v2, double vH) + { if (vH < 0) vH += 1; if (vH > 1) vH -= 1; if (6 * vH < 1) return v1 + (v2 - v1) * 6 * vH; @@ -18,12 +22,14 @@ namespace Material.Colors.ColorManipulation { var l = hsl.L * (1.0 / 100); double r, g, b; - if (s == 0) { + if (s == 0) + { r = l * 255; g = l * 255; b = l * 255; } - else { + else + { double var2; if (l < 0.5) var2 = l * (1 + s); else var2 = l + s - s * l; diff --git a/Material.Colors/ColorManipulation/Lab.cs b/Material.Colors/ColorManipulation/Lab.cs index c4f650c..a58abee 100644 --- a/Material.Colors/ColorManipulation/Lab.cs +++ b/Material.Colors/ColorManipulation/Lab.cs @@ -1,19 +1,23 @@ using System; -namespace Material.Colors.ColorManipulation { - internal struct Lab { +namespace Material.Colors.ColorManipulation +{ + internal struct Lab + { public double L { get; } public double A { get; } public double B { get; } - public Lab(double l, double a, double b) { + public Lab(double l, double a, double b) + { L = l; A = a; B = b; } } - internal class LabConstants { + internal class LabConstants + { public const double Kn = 18; public const double WhitePointX = 0.95047; diff --git a/Material.Colors/ColorManipulation/LabConverter.cs b/Material.Colors/ColorManipulation/LabConverter.cs index 5c188ba..b187d70 100644 --- a/Material.Colors/ColorManipulation/LabConverter.cs +++ b/Material.Colors/ColorManipulation/LabConverter.cs @@ -1,15 +1,20 @@ using System; using Avalonia.Media; -namespace Material.Colors.ColorManipulation { - internal static class LabConverter { - public static Lab ToLab(this Color c) { +namespace Material.Colors.ColorManipulation +{ + internal static class LabConverter + { + public static Lab ToLab(this Color c) + { var xyz = c.ToXyz(); return xyz.ToLab(); } - public static Lab ToLab(this Xyz xyz) { - double XyzLab(double v) { + public static Lab ToLab(this Xyz xyz) + { + double XyzLab(double v) + { if (v > LabConstants.E) return Math.Pow(v, 1 / 3.0); return (v * LabConstants.K + 16) / 116; @@ -25,7 +30,8 @@ namespace Material.Colors.ColorManipulation { return new Lab(l, a, b); } - public static Color ToColor(this Lab lab) { + public static Color ToColor(this Lab lab) + { var xyz = lab.ToXyz(); return xyz.ToColor(); diff --git a/Material.Colors/ColorManipulation/Xyz.cs b/Material.Colors/ColorManipulation/Xyz.cs index cd62fb5..93bb6bb 100644 --- a/Material.Colors/ColorManipulation/Xyz.cs +++ b/Material.Colors/ColorManipulation/Xyz.cs @@ -1,10 +1,13 @@ -namespace Material.Colors.ColorManipulation { - internal struct Xyz { +namespace Material.Colors.ColorManipulation +{ + internal struct Xyz + { public double X { get; } public double Y { get; } public double Z { get; } - public Xyz(double x, double y, double z) { + public Xyz(double x, double y, double z) + { X = x; Y = y; Z = z; diff --git a/Material.Colors/ColorManipulation/XyzConverter.cs b/Material.Colors/ColorManipulation/XyzConverter.cs index fb7c1bf..bbb6aed 100644 --- a/Material.Colors/ColorManipulation/XyzConverter.cs +++ b/Material.Colors/ColorManipulation/XyzConverter.cs @@ -1,16 +1,21 @@ using System; using Avalonia.Media; -namespace Material.Colors.ColorManipulation { - internal static class XyzConverter { - public static Color ToColor(this Xyz xyz) { - double XyzRgb(double d) { +namespace Material.Colors.ColorManipulation +{ + internal static class XyzConverter + { + public static Color ToColor(this Xyz xyz) + { + double XyzRgb(double d) + { if (d > 0.0031308) return 255.0 * (1.055 * Math.Pow(d, 1.0 / 2.4) - 0.055); return 255.0 * (12.92 * d); } - byte Clip(double d) { + byte Clip(double d) + { if (d < 0) return 0; if (d > 255) return 255; return (byte) Math.Round(d); @@ -23,8 +28,10 @@ namespace Material.Colors.ColorManipulation { return Color.FromRgb(Clip(r), Clip(g), Clip(b)); } - public static Xyz ToXyz(this Color c) { - double RgbXyz(double v) { + public static Xyz ToXyz(this Color c) + { + double RgbXyz(double v) + { v /= 255; if (v > 0.04045) return Math.Pow((v + 0.055) / 1.055, 2.4); @@ -41,8 +48,10 @@ namespace Material.Colors.ColorManipulation { return new Xyz(x, y, z); } - public static Xyz ToXyz(this Lab lab) { - double LabXyz(double d) { + public static Xyz ToXyz(this Lab lab) + { + double LabXyz(double d) + { if (d > LabConstants.ECubedRoot) return d * d * d; return (116 * d - 16) / LabConstants.K; diff --git a/Material.Colors/ColorPair.cs b/Material.Colors/ColorPair.cs index 30c540d..48c4b36 100644 --- a/Material.Colors/ColorPair.cs +++ b/Material.Colors/ColorPair.cs @@ -1,32 +1,33 @@ using Avalonia.Media; using Material.Colors.ColorManipulation; -namespace Material.Colors { - public struct ColorPair { - public Color Color { get; set; } +namespace Material.Colors +{ + public struct ColorPair + { + public Color Color { get; } /// /// The foreground or opposite color. If left null, this will be calculated for you. /// Calculated by calling ColorHelper.ContrastingForegroundColor() /// - public Color? ForegroundColor { get; set; } + public Color ForegroundColor { get; } - public static implicit operator ColorPair(Color color) { + public static implicit operator ColorPair(Color color) + { return new ColorPair(color); } - public ColorPair(Color color) { + public ColorPair(Color color) + { Color = color; - ForegroundColor = null; + ForegroundColor = color.PickContrastColor(4.5); } - public ColorPair(Color color, Color? foreground) { + public ColorPair(Color color, Color foreground) + { Color = color; ForegroundColor = foreground; } - - public Color GetForegroundColor() { - return ForegroundColor ?? Color.ContrastingForegroundColor(); - } } } \ No newline at end of file diff --git a/Material.Colors/ISwatch.cs b/Material.Colors/ISwatch.cs index 5187570..ccd0034 100644 --- a/Material.Colors/ISwatch.cs +++ b/Material.Colors/ISwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors { - public interface ISwatch { +namespace Material.Colors +{ + public interface ISwatch + { string Name { get; } IEnumerable Hues { get; } IDictionary Lookup { get; } diff --git a/Material.Colors/Material.Colors.csproj b/Material.Colors/Material.Colors.csproj index 5311729..e222271 100644 --- a/Material.Colors/Material.Colors.csproj +++ b/Material.Colors/Material.Colors.csproj @@ -17,6 +17,6 @@ - + diff --git a/Material.Colors/MaterialColor.cs b/Material.Colors/MaterialColor.cs index 07edcfe..f9c7d71 100644 --- a/Material.Colors/MaterialColor.cs +++ b/Material.Colors/MaterialColor.cs @@ -1,5 +1,7 @@ -namespace Material.Colors { - public enum PrimaryColor { +namespace Material.Colors +{ + public enum PrimaryColor + { Red = MaterialColor.Red500, Pink = MaterialColor.Pink500, Purple = MaterialColor.Purple500, @@ -21,7 +23,8 @@ BlueGrey = MaterialColor.BlueGrey500 } - public enum SecondaryColor { + public enum SecondaryColor + { Red = MaterialColor.RedSecondary, Pink = MaterialColor.PinkSecondary, Purple = MaterialColor.PurpleSecondary, @@ -40,7 +43,8 @@ DeepOrange = MaterialColor.DeepOrangeSecondary } - public enum MaterialColor { + public enum MaterialColor + { Red50, Red100, Red200, diff --git a/Material.Colors/Recommended/AmberSwatch.cs b/Material.Colors/Recommended/AmberSwatch.cs index 04d1cdc..3624e65 100644 --- a/Material.Colors/Recommended/AmberSwatch.cs +++ b/Material.Colors/Recommended/AmberSwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class AmberSwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class AmberSwatch : ISwatch + { public static Color Amber50 { get; } = Color.Parse("#FFF8E1"); public static Color Amber100 { get; } = Color.Parse("#FFECB3"); public static Color Amber200 { get; } = Color.Parse("#FFE082"); @@ -20,7 +22,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Amber"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.Amber50, Amber50}, {MaterialColor.Amber100, Amber100}, {MaterialColor.Amber200, Amber200}, diff --git a/Material.Colors/Recommended/BlueGreySwatch.cs b/Material.Colors/Recommended/BlueGreySwatch.cs index ffad355..41524ec 100644 --- a/Material.Colors/Recommended/BlueGreySwatch.cs +++ b/Material.Colors/Recommended/BlueGreySwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class BlueGreySwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class BlueGreySwatch : ISwatch + { public static Color BlueGrey50 { get; } = Color.Parse("#ECEFF1"); public static Color BlueGrey100 { get; } = Color.Parse("#CFD8DC"); public static Color BlueGrey200 { get; } = Color.Parse("#B0BEC5"); @@ -16,7 +18,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Blue Grey"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.BlueGrey50, BlueGrey50}, {MaterialColor.BlueGrey100, BlueGrey100}, {MaterialColor.BlueGrey200, BlueGrey200}, diff --git a/Material.Colors/Recommended/BlueSwatch.cs b/Material.Colors/Recommended/BlueSwatch.cs index d4124ff..f7c3bea 100644 --- a/Material.Colors/Recommended/BlueSwatch.cs +++ b/Material.Colors/Recommended/BlueSwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class BlueSwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class BlueSwatch : ISwatch + { public static Color Blue50 { get; } = Color.Parse("#E3F2FD"); public static Color Blue100 { get; } = Color.Parse("#BBDEFB"); public static Color Blue200 { get; } = Color.Parse("#90CAF9"); @@ -20,7 +22,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Blue"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.Blue50, Blue50}, {MaterialColor.Blue100, Blue100}, {MaterialColor.Blue200, Blue200}, diff --git a/Material.Colors/Recommended/BrownSwatch.cs b/Material.Colors/Recommended/BrownSwatch.cs index 59db51b..156853b 100644 --- a/Material.Colors/Recommended/BrownSwatch.cs +++ b/Material.Colors/Recommended/BrownSwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class BrownSwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class BrownSwatch : ISwatch + { public static Color Brown50 { get; } = Color.Parse("#EFEBE9"); public static Color Brown100 { get; } = Color.Parse("#D7CCC8"); public static Color Brown200 { get; } = Color.Parse("#BCAAA4"); @@ -16,7 +18,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Brown"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.Brown50, Brown50}, {MaterialColor.Brown100, Brown100}, {MaterialColor.Brown200, Brown200}, diff --git a/Material.Colors/Recommended/CyanSwatch.cs b/Material.Colors/Recommended/CyanSwatch.cs index 1cd4836..b787902 100644 --- a/Material.Colors/Recommended/CyanSwatch.cs +++ b/Material.Colors/Recommended/CyanSwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class CyanSwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class CyanSwatch : ISwatch + { public static Color Cyan50 { get; } = Color.Parse("#E0F7FA"); public static Color Cyan100 { get; } = Color.Parse("#B2EBF2"); public static Color Cyan200 { get; } = Color.Parse("#80DEEA"); @@ -20,7 +22,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Cyan"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.Cyan50, Cyan50}, {MaterialColor.Cyan100, Cyan100}, {MaterialColor.Cyan200, Cyan200}, diff --git a/Material.Colors/Recommended/DeepOrangeSwatch.cs b/Material.Colors/Recommended/DeepOrangeSwatch.cs index 17369f9..5e34487 100644 --- a/Material.Colors/Recommended/DeepOrangeSwatch.cs +++ b/Material.Colors/Recommended/DeepOrangeSwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class DeepOrangeSwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class DeepOrangeSwatch : ISwatch + { public static Color DeepOrange50 { get; } = Color.Parse("#FBE9E7"); public static Color DeepOrange100 { get; } = Color.Parse("#FFCCBC"); public static Color DeepOrange200 { get; } = Color.Parse("#FFAB91"); @@ -20,7 +22,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Deep Orange"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.DeepOrange50, DeepOrange50}, {MaterialColor.DeepOrange100, DeepOrange100}, {MaterialColor.DeepOrange200, DeepOrange200}, diff --git a/Material.Colors/Recommended/DeepPurpleSwatch.cs b/Material.Colors/Recommended/DeepPurpleSwatch.cs index a022e40..6ca758f 100644 --- a/Material.Colors/Recommended/DeepPurpleSwatch.cs +++ b/Material.Colors/Recommended/DeepPurpleSwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class DeepPurpleSwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class DeepPurpleSwatch : ISwatch + { public static Color DeepPurple50 { get; } = Color.Parse("#EDE7F6"); public static Color DeepPurple100 { get; } = Color.Parse("#D1C4E9"); public static Color DeepPurple200 { get; } = Color.Parse("#B39DDB"); @@ -20,7 +22,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Deep Purple"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.DeepPurple50, DeepPurple50}, {MaterialColor.DeepPurple100, DeepPurple100}, {MaterialColor.DeepPurple200, DeepPurple200}, diff --git a/Material.Colors/Recommended/GreenSwatch.cs b/Material.Colors/Recommended/GreenSwatch.cs index 77fb145..9972048 100644 --- a/Material.Colors/Recommended/GreenSwatch.cs +++ b/Material.Colors/Recommended/GreenSwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class GreenSwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class GreenSwatch : ISwatch + { public static Color Green50 { get; } = Color.Parse("#E8F5E9"); public static Color Green100 { get; } = Color.Parse("#C8E6C9"); public static Color Green200 { get; } = Color.Parse("#A5D6A7"); @@ -20,7 +22,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Green"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.Green50, Green50}, {MaterialColor.Green100, Green100}, {MaterialColor.Green200, Green200}, diff --git a/Material.Colors/Recommended/GreySwatch.cs b/Material.Colors/Recommended/GreySwatch.cs index 69cd22e..f202c12 100644 --- a/Material.Colors/Recommended/GreySwatch.cs +++ b/Material.Colors/Recommended/GreySwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class GreySwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class GreySwatch : ISwatch + { public static Color Grey50 { get; } = Color.Parse("#FAFAFA"); public static Color Grey100 { get; } = Color.Parse("#F5F5F5"); public static Color Grey200 { get; } = Color.Parse("#EEEEEE"); @@ -16,7 +18,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Grey"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.Grey50, Grey50}, {MaterialColor.Grey100, Grey100}, {MaterialColor.Grey200, Grey200}, diff --git a/Material.Colors/Recommended/IndigoSwatch.cs b/Material.Colors/Recommended/IndigoSwatch.cs index 830f9f2..d019dd1 100644 --- a/Material.Colors/Recommended/IndigoSwatch.cs +++ b/Material.Colors/Recommended/IndigoSwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class IndigoSwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class IndigoSwatch : ISwatch + { public static Color Indigo50 { get; } = Color.Parse("#E8EAF6"); public static Color Indigo100 { get; } = Color.Parse("#C5CAE9"); public static Color Indigo200 { get; } = Color.Parse("#9FA8DA"); @@ -20,7 +22,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Indigo"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.Indigo50, Indigo50}, {MaterialColor.Indigo100, Indigo100}, {MaterialColor.Indigo200, Indigo200}, diff --git a/Material.Colors/Recommended/LightBlueSwatch.cs b/Material.Colors/Recommended/LightBlueSwatch.cs index f199f4e..d0bfde1 100644 --- a/Material.Colors/Recommended/LightBlueSwatch.cs +++ b/Material.Colors/Recommended/LightBlueSwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class LightBlueSwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class LightBlueSwatch : ISwatch + { public static Color LightBlue50 { get; } = Color.Parse("#E1F5FE"); public static Color LightBlue100 { get; } = Color.Parse("#B3E5FC"); public static Color LightBlue200 { get; } = Color.Parse("#81D4FA"); @@ -20,7 +22,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Light Blue"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.LightBlue50, LightBlue50}, {MaterialColor.LightBlue100, LightBlue100}, {MaterialColor.LightBlue200, LightBlue200}, diff --git a/Material.Colors/Recommended/LightGreenSwatch.cs b/Material.Colors/Recommended/LightGreenSwatch.cs index c8413de..2ab762a 100644 --- a/Material.Colors/Recommended/LightGreenSwatch.cs +++ b/Material.Colors/Recommended/LightGreenSwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class LightGreenSwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class LightGreenSwatch : ISwatch + { public static Color LightGreen50 { get; } = Color.Parse("#F1F8E9"); public static Color LightGreen100 { get; } = Color.Parse("#DCEDC8"); public static Color LightGreen200 { get; } = Color.Parse("#C5E1A5"); @@ -20,7 +22,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Light Green"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.LightGreen50, LightGreen50}, {MaterialColor.LightGreen100, LightGreen100}, {MaterialColor.LightGreen200, LightGreen200}, diff --git a/Material.Colors/Recommended/LimeSwatch.cs b/Material.Colors/Recommended/LimeSwatch.cs index d2f2554..1da6fd0 100644 --- a/Material.Colors/Recommended/LimeSwatch.cs +++ b/Material.Colors/Recommended/LimeSwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class LimeSwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class LimeSwatch : ISwatch + { public static Color Lime50 { get; } = Color.Parse("#F9FBE7"); public static Color Lime100 { get; } = Color.Parse("#F0F4C3"); public static Color Lime200 { get; } = Color.Parse("#E6EE9C"); @@ -20,7 +22,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Lime"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.Lime50, Lime50}, {MaterialColor.Lime100, Lime100}, {MaterialColor.Lime200, Lime200}, diff --git a/Material.Colors/Recommended/OrangeSwatch.cs b/Material.Colors/Recommended/OrangeSwatch.cs index 8cbe583..c9a8e9c 100644 --- a/Material.Colors/Recommended/OrangeSwatch.cs +++ b/Material.Colors/Recommended/OrangeSwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class OrangeSwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class OrangeSwatch : ISwatch + { public static Color Orange50 { get; } = Color.Parse("#FFF3E0"); public static Color Orange100 { get; } = Color.Parse("#FFE0B2"); public static Color Orange200 { get; } = Color.Parse("#FFCC80"); @@ -20,7 +22,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Orange"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.Orange50, Orange50}, {MaterialColor.Orange100, Orange100}, {MaterialColor.Orange200, Orange200}, diff --git a/Material.Colors/Recommended/PinkSwatch.cs b/Material.Colors/Recommended/PinkSwatch.cs index 78291d4..60c23de 100644 --- a/Material.Colors/Recommended/PinkSwatch.cs +++ b/Material.Colors/Recommended/PinkSwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class PinkSwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class PinkSwatch : ISwatch + { public static Color Pink50 { get; } = Color.Parse("#FCE4EC"); public static Color Pink100 { get; } = Color.Parse("#F8BBD0"); public static Color Pink200 { get; } = Color.Parse("#F48FB1"); @@ -20,7 +22,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Pink"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.Pink50, Pink50}, {MaterialColor.Pink100, Pink100}, {MaterialColor.Pink200, Pink200}, diff --git a/Material.Colors/Recommended/PurpleSwatch.cs b/Material.Colors/Recommended/PurpleSwatch.cs index ef13730..5fffd77 100644 --- a/Material.Colors/Recommended/PurpleSwatch.cs +++ b/Material.Colors/Recommended/PurpleSwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class PurpleSwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class PurpleSwatch : ISwatch + { public static Color Purple50 { get; } = Color.Parse("#F3E5F5"); public static Color Purple100 { get; } = Color.Parse("#E1BEE7"); public static Color Purple200 { get; } = Color.Parse("#CE93D8"); @@ -20,7 +22,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Purple"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.Purple50, Purple50}, {MaterialColor.Purple100, Purple100}, {MaterialColor.Purple200, Purple200}, diff --git a/Material.Colors/Recommended/RedSwatch.cs b/Material.Colors/Recommended/RedSwatch.cs index 3eb3582..2dba699 100644 --- a/Material.Colors/Recommended/RedSwatch.cs +++ b/Material.Colors/Recommended/RedSwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class RedSwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class RedSwatch : ISwatch + { public static Color Red50 { get; } = Color.Parse("#FFEBEE"); public static Color Red100 { get; } = Color.Parse("#FFCDD2"); public static Color Red200 { get; } = Color.Parse("#EF9A9A"); @@ -20,7 +22,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Red"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.Red50, Red50}, {MaterialColor.Red100, Red100}, {MaterialColor.Red200, Red200}, diff --git a/Material.Colors/Recommended/TealSwatch.cs b/Material.Colors/Recommended/TealSwatch.cs index 6407c91..d3d715f 100644 --- a/Material.Colors/Recommended/TealSwatch.cs +++ b/Material.Colors/Recommended/TealSwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class TealSwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class TealSwatch : ISwatch + { public static Color Teal50 { get; } = Color.Parse("#E0F2F1"); public static Color Teal100 { get; } = Color.Parse("#B2DFDB"); public static Color Teal200 { get; } = Color.Parse("#80CBC4"); @@ -20,7 +22,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Teal"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.Teal50, Teal50}, {MaterialColor.Teal100, Teal100}, {MaterialColor.Teal200, Teal200}, diff --git a/Material.Colors/Recommended/YellowSwatch.cs b/Material.Colors/Recommended/YellowSwatch.cs index 88280bb..704b349 100644 --- a/Material.Colors/Recommended/YellowSwatch.cs +++ b/Material.Colors/Recommended/YellowSwatch.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using Avalonia.Media; -namespace Material.Colors.Recommended { - public class YellowSwatch : ISwatch { +namespace Material.Colors.Recommended +{ + public class YellowSwatch : ISwatch + { public static Color Yellow50 { get; } = Color.Parse("#FFFDE7"); public static Color Yellow100 { get; } = Color.Parse("#FFF9C4"); public static Color Yellow200 { get; } = Color.Parse("#FFF59D"); @@ -20,7 +22,8 @@ namespace Material.Colors.Recommended { public string Name { get; } = "Yellow"; - public IDictionary Lookup { get; } = new Dictionary { + public IDictionary Lookup { get; } = new Dictionary + { {MaterialColor.Yellow50, Yellow50}, {MaterialColor.Yellow100, Yellow100}, {MaterialColor.Yellow200, Yellow200}, diff --git a/Material.Colors/SwatchHelper.cs b/Material.Colors/SwatchHelper.cs index c9f45c5..101c78d 100644 --- a/Material.Colors/SwatchHelper.cs +++ b/Material.Colors/SwatchHelper.cs @@ -3,9 +3,12 @@ using System.Linq; using Avalonia.Media; using Material.Colors.Recommended; -namespace Material.Colors { - public static class SwatchHelper { - public static IEnumerable Swatches { get; } = new ISwatch[] { +namespace Material.Colors +{ + public static class SwatchHelper + { + public static IEnumerable Swatches { get; } = new ISwatch[] + { new RedSwatch(), new PinkSwatch(), new PurpleSwatch(), @@ -27,6 +30,7 @@ namespace Material.Colors { new BlueGreySwatch() }; - public static IDictionary Lookup { get; } = Swatches.SelectMany(o => o.Lookup).ToDictionary(o => o.Key, o => o.Value); + public static IDictionary Lookup { get; } = + Swatches.SelectMany(o => o.Lookup).ToDictionary(o => o.Key, o => o.Value); } } \ No newline at end of file diff --git a/Material.DataGrid/Material.DataGrid.csproj b/Material.DataGrid/Material.DataGrid.csproj index eb5ccb3..5cf4807 100644 --- a/Material.DataGrid/Material.DataGrid.csproj +++ b/Material.DataGrid/Material.DataGrid.csproj @@ -21,8 +21,8 @@ - - + + diff --git a/Material.Demo/App.axaml b/Material.Demo/App.axaml index 0ef0a1c..f8810a1 100644 --- a/Material.Demo/App.axaml +++ b/Material.Demo/App.axaml @@ -5,11 +5,14 @@ x:Class="Material.Demo.App"> - + + + + + + + + Your lucky number: + + + + + - - + - + - + - - + diff --git a/Material.Demo/Models/FeatureStatusModels.cs b/Material.Demo/Models/FeatureStatusModels.cs index 9193655..43c6357 100644 --- a/Material.Demo/Models/FeatureStatusModels.cs +++ b/Material.Demo/Models/FeatureStatusModels.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Text; +using System.ComponentModel; namespace Material.Demo.Models { @@ -9,15 +6,14 @@ namespace Material.Demo.Models { Yes, No, - [Description("N/A")] - NA, - [Description("Not Fully")] - NotFully + [Description("N/A")] NA, + [Description("Not Fully")] NotFully } + public class FeatureStatusModels { public string FeatureName { get; internal set; } public StatusEnum IsReady { get; internal set; } - public StatusEnum IsAnimated { get; internal set; } + public StatusEnum IsAnimated { get; internal set; } } -} +} \ No newline at end of file diff --git a/Material.Demo/Models/PackIconKindGroup.cs b/Material.Demo/Models/PackIconKindGroup.cs index b04b2cc..375cdca 100644 --- a/Material.Demo/Models/PackIconKindGroup.cs +++ b/Material.Demo/Models/PackIconKindGroup.cs @@ -2,19 +2,21 @@ using System; using System.Collections.Generic; using System.Linq; using Material.Icons; -using Material.Icons.Avalonia; -namespace Material.Demo.Models { - public class MaterialIconKindGroup { - public MaterialIconKindGroup(IEnumerable kinds) { +namespace Material.Demo.Models +{ + public class MaterialIconKindGroup + { + public MaterialIconKindGroup(IEnumerable kinds) + { if (kinds is null) throw new ArgumentNullException(nameof(kinds)); var allValues = kinds.ToList(); if (!allValues.Any()) throw new ArgumentException($"{nameof(kinds)} must contain at least one value"); Kind = allValues.First(); KindValue = Enum.Parse(Kind); Aliases = allValues - .OrderBy(x => x, StringComparer.InvariantCultureIgnoreCase) - .ToArray(); + .OrderBy(x => x, StringComparer.InvariantCultureIgnoreCase) + .ToArray(); } public string Kind { get; } diff --git a/Material.Demo/Models/Sample2Model.cs b/Material.Demo/Models/Sample2Model.cs index 11e921b..033e986 100644 --- a/Material.Demo/Models/Sample2Model.cs +++ b/Material.Demo/Models/Sample2Model.cs @@ -1,8 +1,12 @@ -namespace Material.Demo.Models { - public class Sample2Model { - public Sample2Model(int number) { +namespace Material.Demo.Models +{ + public class Sample2Model + { + public Sample2Model(int number) + { Number = number; } + public int Number { get; set; } } } \ No newline at end of file diff --git a/Material.Demo/Models/SideSheetDemoViewModel.cs b/Material.Demo/Models/SideSheetDemoViewModel.cs index 8117ab9..cc7fd1f 100644 --- a/Material.Demo/Models/SideSheetDemoViewModel.cs +++ b/Material.Demo/Models/SideSheetDemoViewModel.cs @@ -7,16 +7,17 @@ public string Header => _header; public string ContentHeader { get; set; } = "What is Lorem Ipsum?"; - - public string ImportantInfos { get; set; } = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."; + + public string ImportantInfos { get; set; } = + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."; } - + // Data context for SideSheet demo page public class SideSheetDemoViewModel : ViewModelBase { private SideSheetData _information = new SideSheetData(); public SideSheetData Information => _information; - + private bool _sideInfoOpened = false; public bool SideInfoOpened diff --git a/Material.Demo/Pages/ButtonsDemo.axaml b/Material.Demo/Pages/ButtonsDemo.axaml index afd30f5..bbd073f 100644 --- a/Material.Demo/Pages/ButtonsDemo.axaml +++ b/Material.Demo/Pages/ButtonsDemo.axaml @@ -7,9 +7,26 @@ xmlns:assist="clr-namespace:Material.Styles.Assists;assembly=Material.Styles" xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:showMeTheXaml="clr-namespace:ShowMeTheXaml;assembly=ShowMeTheXaml.Avalonia" + xmlns:controls="clr-namespace:Material.Styles.Controls;assembly=Material.Styles" x:Class="Material.Demo.Pages.ButtonsDemo"> + + + + + + + + @@ -27,7 +44,7 @@ - - - + + + + + + + + Top level dialog with custom corner radius, using OpenDialog, passing content via the Parameter. You can pass a view model, provided a corresponding DataTemplate can be found in the scope of the root DialogHost. + + + + - - - + + - - - - - - + - + CornerRadius="32" Margin="8"> - @@ -341,5 +345,7 @@ + + diff --git a/Material.Demo/Pages/FieldsDemo.axaml.cs b/Material.Demo/Pages/FieldsDemo.axaml.cs index 6bcb003..c05aa92 100644 --- a/Material.Demo/Pages/FieldsDemo.axaml.cs +++ b/Material.Demo/Pages/FieldsDemo.axaml.cs @@ -1,5 +1,4 @@ -using Avalonia; -using Avalonia.Controls; +using Avalonia.Controls; using Avalonia.Markup.Xaml; using Material.Demo.ViewModels; @@ -19,4 +18,4 @@ namespace Material.Demo.Pages AvaloniaXamlLoader.Load(this); } } -} +} \ No newline at end of file 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"> + - - - - - - - - - - - - + - - - + - + - + - + \ No newline at end of file diff --git a/Material.Demo/Pages/IconsDemo.axaml.cs b/Material.Demo/Pages/IconsDemo.axaml.cs index 3a5cb92..fae7360 100644 --- a/Material.Demo/Pages/IconsDemo.axaml.cs +++ b/Material.Demo/Pages/IconsDemo.axaml.cs @@ -1,32 +1,36 @@ #nullable enable -using System; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Markup.Xaml; using Avalonia.Threading; using Material.Demo.ViewModels; -namespace Material.Demo.Pages { - public class IconsDemo : UserControl { - public IconsDemo() { +namespace Material.Demo.Pages +{ + public class IconsDemo : UserControl + { + public IconsDemo() + { InitializeComponent(); - + DataContext = new IconsDemoViewModel(); } - + private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } - private void Search_OnKeyDown(object? sender, KeyEventArgs e) { - var textBox = (TextBox)sender!; + private void Search_OnKeyDown(object? sender, KeyEventArgs e) + { + var textBox = (TextBox) sender!; if (e.Key == Key.Enter) this.Get - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Material.Styles/Card.xaml b/Material.Styles/Card.xaml index 7965993..7ea7112 100644 --- a/Material.Styles/Card.xaml +++ b/Material.Styles/Card.xaml @@ -1,8 +1,9 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + - + - \ No newline at end of file diff --git a/Material.Styles/CircleClockPicker.axaml b/Material.Styles/CircleClockPicker.axaml new file mode 100644 index 0000000..d372961 --- /dev/null +++ b/Material.Styles/CircleClockPicker.axaml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + 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..53e3b4a 100644 --- a/Material.Styles/ColorZone.xaml +++ b/Material.Styles/ColorZone.xaml @@ -2,10 +2,10 @@ 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/ColorZone.xaml.cs b/Material.Styles/ColorZone.xaml.cs deleted file mode 100644 index eb71854..0000000 --- a/Material.Styles/ColorZone.xaml.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Avalonia; -using Avalonia.Controls; - -namespace Material.Styles { - public enum ColorZoneMode - { - Standard, - Inverted, - PrimaryLight, - PrimaryMid, - PrimaryDark, - Accent, - Light, - Dark, - 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)); - - public ColorZoneMode Mode - { - get => GetValue(ModeProperty); - set => SetValue(ModeProperty, value); - } - } -} \ No newline at end of file diff --git a/Material.Styles/ComboBox.xaml b/Material.Styles/ComboBox.xaml index 4b6148b..4102dee 100644 --- a/Material.Styles/ComboBox.xaml +++ b/Material.Styles/ComboBox.xaml @@ -1,410 +1,519 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Material.Styles/ContentExpandControl.axaml b/Material.Styles/ContentExpandControl.axaml new file mode 100644 index 0000000..75970ef --- /dev/null +++ b/Material.Styles/ContentExpandControl.axaml @@ -0,0 +1,19 @@ + + + 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/Controls/Arc.cs b/Material.Styles/Controls/Arc.cs index 0f5d768..0fbd63a 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, - StartAngleProperty, - SweepAngleProperty); + 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 sweep = (offset + SweepAngle) / 360; - - var path = new StringBuilder($"M {halfW.ToString(CultureInfo.InvariantCulture)} {halfH.ToString(CultureInfo.InvariantCulture)}"); - - int length = 24; - - for (int i = 0; i < length; i++) - { - var limit = offset / 360 + i / (double)length; - - 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)}"); - } - - 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)}"); + var geometry = new StreamGeometry(); - path.Append(" Z"); - var result = path.ToString().Replace(',', '.'); - return StreamGeometry.Parse(result); + var sweep = SweepAngle; + + if (sweep == 0) + return geometry; + + using var ctx = geometry.Open(); + + // Center point + ctx.BeginFigure(new Point(halfW, halfH), true); + + const int len = 24; + + for (var i = 0; i < len; i++) + { + var l = offset + sweep * i / len; + + ctx.LineTo(DegToPoint(l, halfW, halfH)); + } + + ctx.LineTo(DegToPoint(offset + sweep, halfW, halfH)); + + ctx.EndFigure(true); + return geometry; + } + + 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 79% rename from Material.Styles/Card.xaml.cs rename to Material.Styles/Controls/Card.cs index e13c132..5432049 100644 --- a/Material.Styles/Card.xaml.cs +++ b/Material.Styles/Controls/Card.cs @@ -1,10 +1,11 @@ using Avalonia; using Avalonia.Controls; -using Avalonia.Controls.Primitives; -namespace Material.Styles { - public class Card : ContentControl { - public static readonly StyledProperty InsideClippingProperty = +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..72af3cf --- /dev/null +++ b/Material.Styles/Controls/CircleClockPicker.cs @@ -0,0 +1,286 @@ +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; + +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..132ec9b --- /dev/null +++ b/Material.Styles/Controls/CircleClockPickerCell.cs @@ -0,0 +1,61 @@ +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/Controls/ColorZone.cs b/Material.Styles/Controls/ColorZone.cs new file mode 100644 index 0000000..3d1a9d8 --- /dev/null +++ b/Material.Styles/Controls/ColorZone.cs @@ -0,0 +1,173 @@ +using System; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Data; +using Avalonia.Media; +using Avalonia.Threading; +using Material.Styles.Themes; + +namespace Material.Styles.Controls +{ + public enum ColorZoneMode + { + Standard, + Inverted, + PrimaryLight, + PrimaryMid, + PrimaryDark, + Accent, + Light, + Dark, + Custom + } + + public class ColorZone : ContentControl + { + public static readonly StyledProperty ModeProperty = + AvaloniaProperty.Register(nameof(Mode)); + + public ColorZoneMode Mode + { + get => GetValue(ModeProperty); + set => SetValue(ModeProperty, value); + } + + private IDisposable? _subscription; + + static ColorZone() + { + ModeProperty.Changed.Subscribe(OnNext); + } + + private static void OnNext(AvaloniaPropertyChangedEventArgs arg) + { + if (arg.Sender is not ColorZone control) + return; + + var resources = Application.Current!.LocateMaterialTheme(); + control.OnThemeChanged(resources); + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + OnModeChanged(null); + + DisposeSubscription(); + + var resources = Application.Current!.LocateMaterialTheme(); + _subscription = resources.ThemeChangedObservable.Subscribe(OnThemeChanged); + + base.OnAttachedToVisualTree(e); + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + DisposeSubscription(); + + base.OnDetachedFromVisualTree(e); + } + + private void DisposeSubscription() + { + _subscription?.Dispose(); + _subscription = null; + } + + private void OnThemeChanged(MaterialThemeBase theme) + { + Dispatcher.UIThread.InvokeAsync(delegate { OnModeChanged(theme); }); + } + + private void OnModeChanged(MaterialThemeBase? theme) + { + var colorZone = this; + theme ??= Application.Current!.LocateMaterialTheme(); + + var resources = theme; + + var foregroundProperty = TemplatedControl.ForegroundProperty; + + switch (colorZone.Mode) + { + case ColorZoneMode.Standard: + { + SetValueInternal(BackgroundProperty, GetBrushResource(resources, "MaterialDesignPaper")); + SetValueInternal(foregroundProperty, GetBrushResource(resources, "MaterialDesignBody")); + } + break; + + case ColorZoneMode.Inverted: + { + SetValueInternal(BackgroundProperty, GetBrushResource(resources, "MaterialDesignBody")); + SetValueInternal(foregroundProperty, GetBrushResource(resources, "MaterialDesignPaper")); + } + break; + + case ColorZoneMode.Light: + { + SetValueInternal(BackgroundProperty, GetBrushResource(resources, "MaterialDesignLightBackground")); + SetValueInternal(foregroundProperty, GetBrushResource(resources, "MaterialDesignLightForeground")); + } + break; + + case ColorZoneMode.Dark: + { + SetValueInternal(BackgroundProperty, GetBrushResource(resources, "MaterialDesignDarkBackground")); + SetValueInternal(foregroundProperty, GetBrushResource(resources, "MaterialDesignDarkForeground")); + } + break; + + case ColorZoneMode.PrimaryLight: + { + SetValueInternal(BackgroundProperty, GetBrushResource(resources, "PrimaryHueLightBrush")); + SetValueInternal(foregroundProperty, GetBrushResource(resources, "PrimaryHueLightForegroundBrush")); + } + break; + + case ColorZoneMode.PrimaryMid: + { + SetValueInternal(BackgroundProperty, GetBrushResource(resources, "PrimaryHueMidBrush")); + SetValueInternal(foregroundProperty, GetBrushResource(resources, "PrimaryHueMidForegroundBrush")); + } + break; + + case ColorZoneMode.PrimaryDark: + { + SetValueInternal(BackgroundProperty, GetBrushResource(resources, "PrimaryHueDarkBrush")); + SetValueInternal(foregroundProperty, GetBrushResource(resources, "PrimaryHueDarkForegroundBrush")); + } + break; + + case ColorZoneMode.Accent: + { + SetValueInternal(BackgroundProperty, GetBrushResource(resources, "SecondaryHueMidBrush")); + SetValueInternal(foregroundProperty, GetBrushResource(resources, "SecondaryHueMidForegroundBrush")); + } + break; + + case ColorZoneMode.Custom: + break; + + default: + throw new ArgumentOutOfRangeException(); + } + } + + private void SetValueInternal(AvaloniaProperty property, object? value) + { + SetValue(property, value, BindingPriority.Style); + } + + private static IBrush? GetBrushResource(IResourceNode theme, string name) + { + if (!theme.TryGetResource(name, out var resource)) + return null; + + if (resource is IBrush brush) + return brush; + + return null; + } + } +} \ No newline at end of file diff --git a/Material.Styles/Controls/ContentExpandControl.cs b/Material.Styles/Controls/ContentExpandControl.cs new file mode 100644 index 0000000..127a9e5 --- /dev/null +++ b/Material.Styles/Controls/ContentExpandControl.cs @@ -0,0 +1,77 @@ +using System; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Layout; + +namespace Material.Styles.Controls +{ + public class ContentExpandControl : ContentControl + { + public static readonly StyledProperty MultiplierProperty = + AvaloniaProperty.Register(nameof(Multiplier)); + + public double Multiplier + { + get => GetValue(MultiplierProperty); + set => SetValue(MultiplierProperty, value); + } + + public static readonly StyledProperty OrientationProperty = + AvaloniaProperty.Register(nameof(Orientation)); + + public Orientation Orientation + { + get => GetValue(OrientationProperty); + set => SetValue(OrientationProperty, value); + } + + static ContentExpandControl() + { + AffectsArrange(MultiplierProperty, + OrientationProperty); + + AffectsMeasure(MultiplierProperty, + OrientationProperty); + } + + protected override void ArrangeCore(Rect finalRect) + { + base.ArrangeCore(finalRect); + } + + protected override Size MeasureCore(Size availableSize) + { + var result = base.MeasureCore(availableSize); + return result; + } + + protected override Size ArrangeOverride(Size finalSize) + { + var result = base.ArrangeOverride(finalSize); + return result; + } + + protected override Size MeasureOverride(Size availableSize) + { + var result = base.MeasureOverride(availableSize); + + var w = result.Width; + var h = result.Height; + + switch (Orientation) + { + case Orientation.Horizontal: + w *= Multiplier; + break; + + case Orientation.Vertical: + h *= Multiplier; + break; + default: + throw new ArgumentOutOfRangeException(); + } + + return new Size(w, h); + } + } +} \ No newline at end of file diff --git a/Material.Styles/Controls/Extensions/MaterialInternalIconExtension.cs b/Material.Styles/Controls/Extensions/MaterialInternalIconExtension.cs index d5ceb34..fedc1ca 100644 --- a/Material.Styles/Controls/Extensions/MaterialInternalIconExtension.cs +++ b/Material.Styles/Controls/Extensions/MaterialInternalIconExtension.cs @@ -5,23 +5,27 @@ namespace Material.Styles.Controls.Extensions { public class MaterialInternalIconExtension : MarkupExtension { - public MaterialInternalIconExtension() { } - public MaterialInternalIconExtension(string kind) { + public MaterialInternalIconExtension() + { + } + + public MaterialInternalIconExtension(string kind) + { Kind = kind; } - public MaterialInternalIconExtension(string kind, double? size) { + public MaterialInternalIconExtension(string kind, double? size) + { Kind = kind; Size = size; } - - [ConstructorArgument("kind")] - public string Kind { get; set; } - [ConstructorArgument("size")] - public double? Size { get; set; } - - public override object ProvideValue(IServiceProvider serviceProvider) { + [ConstructorArgument("kind")] public string Kind { get; set; } + + [ConstructorArgument("size")] public double? Size { get; set; } + + public override object ProvideValue(IServiceProvider serviceProvider) + { var result = new MaterialInternalIcon { Kind = Kind 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/MaterialInternalIcon.cs b/Material.Styles/Controls/MaterialInternalIcon.cs index 7de852f..ec2fc0d 100644 --- a/Material.Styles/Controls/MaterialInternalIcon.cs +++ b/Material.Styles/Controls/MaterialInternalIcon.cs @@ -10,30 +10,35 @@ namespace Material.Styles.Controls public class MaterialInternalIcon : TemplatedControl { private static readonly Lazy> _dataSetInstance = new(IconsDataSet.CreateDataSet); - - static MaterialInternalIcon() { + + static MaterialInternalIcon() + { KindProperty.Changed.Subscribe(args => (args.Sender as MaterialInternalIcon)?.UpdateData()); } - public static readonly AvaloniaProperty KindProperty = AvaloniaProperty.Register(nameof(Kind)); + public static readonly AvaloniaProperty KindProperty = + AvaloniaProperty.Register(nameof(Kind)); /// /// Gets or sets the icon to display. /// - public string Kind { + public string Kind + { get => (string) GetValue(KindProperty); set => SetValue(KindProperty, value); } private static readonly AvaloniaProperty - DataProperty = AvaloniaProperty.RegisterDirect(nameof(Data), icon => icon.Data); + DataProperty = + AvaloniaProperty.RegisterDirect(nameof(Data), icon => icon.Data); private Geometry? _data; /// /// Gets the icon path data for the current . /// - public Geometry? Data { + public Geometry? Data + { get { _data = _data switch @@ -46,7 +51,8 @@ namespace Material.Styles.Controls private set => SetAndRaise(DataProperty, ref _data, value); } - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { base.OnApplyTemplate(e); UpdateData(); } @@ -57,7 +63,7 @@ namespace Material.Styles.Controls return; string data = null; - + if (_dataSetInstance.Value?.TryGetValue(Kind, out data) ?? false) Data = Geometry.Parse(data); else diff --git a/Material.Styles/Controls/MaterialUnderline.cs b/Material.Styles/Controls/MaterialUnderline.cs new file mode 100644 index 0000000..04fc4f4 --- /dev/null +++ b/Material.Styles/Controls/MaterialUnderline.cs @@ -0,0 +1,57 @@ +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/Controls/NavigationDrawer.cs b/Material.Styles/Controls/NavigationDrawer.cs new file mode 100644 index 0000000..4158d02 --- /dev/null +++ b/Material.Styles/Controls/NavigationDrawer.cs @@ -0,0 +1,338 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Metadata; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.Templates; +using Avalonia.Interactivity; +using Avalonia.Metadata; + +namespace Material.Styles.Controls +{ + [PseudoClasses(":open", ":closed", ":left", ":right", ":left-expand", ":right-expand")] + public class NavigationDrawer : ContentControl + { + /// + /// Internal use! + /// This property is used to binding the margin of inner content. + /// + public static readonly StyledProperty ContentMarginProperty = + AvaloniaProperty.Register(nameof(ContentMargin)); + + public static readonly StyledProperty LeftDrawerContentProperty = + AvaloniaProperty.Register(nameof(LeftDrawerContent)); + + public static readonly StyledProperty LeftDrawerContentTemplateProperty = + AvaloniaProperty.Register(nameof(LeftDrawerContentTemplate)); + + public static readonly StyledProperty LeftDrawerOpenedProperty = + AvaloniaProperty.Register(nameof(LeftDrawerOpened)); + + public static readonly StyledProperty LeftDrawerWidthProperty = + AvaloniaProperty.Register(nameof(LeftDrawerWidth)); + + public static readonly StyledProperty LeftDrawerExpandThresholdWidthProperty = + AvaloniaProperty.Register(nameof(LeftDrawerExpandThresholdWidth)); + + + public static readonly StyledProperty RightDrawerContentProperty = + AvaloniaProperty.Register(nameof(RightDrawerContent)); + + public static readonly StyledProperty RightDrawerContentTemplateProperty = + AvaloniaProperty.Register(nameof(RightDrawerContentTemplate)); + + public static readonly StyledProperty RightDrawerOpenedProperty = + AvaloniaProperty.Register(nameof(RightDrawerOpened)); + + public static readonly StyledProperty RightDrawerWidthProperty = + AvaloniaProperty.Register(nameof(RightDrawerWidth)); + + public static readonly StyledProperty RightDrawerExpandThresholdWidthProperty = + AvaloniaProperty.Register(nameof(RightDrawerExpandThresholdWidth)); + + /// + /// Internal use! + /// This property is used to binding the margin of inner content. + /// + public Thickness ContentMargin + { + get => GetValue(ContentMarginProperty); + set => SetValue(ContentMarginProperty, value); + } + + /// + /// Gets or sets the content to display. + /// + [DependsOn(nameof(LeftDrawerContentTemplate))] + public object LeftDrawerContent + { + get => GetValue(LeftDrawerContentProperty); + set => SetValue(LeftDrawerContentProperty, value); + } + + /// + /// Gets or sets the data template used to display the content of the control. + /// + public IDataTemplate LeftDrawerContentTemplate + { + get => GetValue(LeftDrawerContentTemplateProperty); + set => SetValue(LeftDrawerContentTemplateProperty, value); + } + + public bool LeftDrawerOpened + { + get => GetValue(LeftDrawerOpenedProperty); + set => SetValue(LeftDrawerOpenedProperty, value); + } + + public double LeftDrawerWidth + { + get => GetValue(LeftDrawerWidthProperty); + set => SetValue(LeftDrawerWidthProperty, value); + } + + /// + ///

Get or sets the width threshold of the NavigationDrawer for expand left drawer automatically. Most used on desktop application.

+ ///

For more information, please visit material.io - Standard navigation drawer, Permanently visible page.

+ /// Use it on desktop application is recommended!! + ///
+ public double? LeftDrawerExpandThresholdWidth + { + get => GetValue(LeftDrawerExpandThresholdWidthProperty); + set => SetValue(LeftDrawerExpandThresholdWidthProperty, value); + } + + /// + /// Gets or sets the content to display. + /// + [DependsOn(nameof(RightDrawerContentTemplate))] + public object RightDrawerContent + { + get => GetValue(RightDrawerContentProperty); + set => SetValue(RightDrawerContentProperty, value); + } + + /// + /// Gets or sets the data template used to display the content of the control. + /// + public IDataTemplate RightDrawerContentTemplate + { + get => GetValue(RightDrawerContentTemplateProperty); + set => SetValue(RightDrawerContentTemplateProperty, value); + } + + public bool RightDrawerOpened + { + get => GetValue(RightDrawerOpenedProperty); + set => SetValue(RightDrawerOpenedProperty, value); + } + + public double RightDrawerWidth + { + get => GetValue(RightDrawerWidthProperty); + set => SetValue(RightDrawerWidthProperty, value); + } + + /// + ///

Get or sets the width threshold of the NavigationDrawer for expand right drawer automatically. Most used on desktop application.

+ ///

For more information, please visit material.io - Standard navigation drawer, Permanently visible page.

+ /// This feature is not recommended if your application is Left-to-Right language orientated. Reference: material.io. + ///
+ public double? RightDrawerExpandThresholdWidth + { + get => GetValue(RightDrawerExpandThresholdWidthProperty); + set => SetValue(RightDrawerExpandThresholdWidthProperty, value); + } + + /// + /// Closes the left or right drawer, it wont be closed if it is permanent visible. + /// + /// set it to false for close the right drawer, otherwise it closes the left drawer. + public void OptionalCloseDrawer(bool isLeftDrawer = true) + { + switch (isLeftDrawer) + { + case true: + OptionalCloseLeftDrawer(); + break; + case false: + OptionalCloseRightDrawer(); + break; + } + } + + /// + /// Close the left drawer, it wont be closed if it is permanent visible. + /// + public void OptionalCloseLeftDrawer() + { + if (_isLeftDrawerDesktopExpanded) + return; + + LeftDrawerOpened = false; + } + + /// + /// Close the right drawer, it wont be closed if it is permanent visible. + /// + public void OptionalCloseRightDrawer() + { + if (_isRightDrawerDesktopExpanded) + return; + + RightDrawerOpened = false; + } + + /// + /// Switch visibility of the left drawer. + /// + public void SwitchLeftDrawerOpened() + { + LeftDrawerOpened = !LeftDrawerOpened; + } + + /// + /// Switch visibility of the right drawer. + /// + public void SwitchRightDrawerOpened() + { + RightDrawerOpened = !RightDrawerOpened; + } + + private bool _isLeftDrawerDesktopExpanded; + private bool _isRightDrawerDesktopExpanded; + + static NavigationDrawer() + { + BoundsProperty.Changed.AddClassHandler(OnDrawerResized); + + LeftDrawerWidthProperty.Changed.AddClassHandler(OnDrawerWidthChanged); + RightDrawerWidthProperty.Changed.AddClassHandler(OnDrawerWidthChanged); + + LeftDrawerOpenedProperty.Changed.AddClassHandler(OnDrawerOpenedChanged); + RightDrawerOpenedProperty.Changed.AddClassHandler(OnDrawerOpenedChanged); + + LeftDrawerExpandThresholdWidthProperty.Changed.AddClassHandler( + OnDrawerExpandThresholdWidthChanged); + RightDrawerExpandThresholdWidthProperty.Changed.AddClassHandler( + OnDrawerExpandThresholdWidthChanged); + } + + private static void OnDrawerResized(NavigationDrawer drawer, AvaloniaPropertyChangedEventArgs args) + { + drawer.UpdateDesktopExpand(drawer.Bounds.Width); + drawer.UpdateContentMargin(); + } + + private static void OnDrawerWidthChanged(NavigationDrawer drawer, AvaloniaPropertyChangedEventArgs args) + { + if (drawer.Classes.Contains(":closed")) + return; + + drawer.UpdateContentMargin(); + } + + private static void OnDrawerExpandThresholdWidthChanged( + NavigationDrawer drawer, AvaloniaPropertyChangedEventArgs args) + { + drawer.UpdateDesktopExpand(drawer.Bounds.Width); + } + + private static void OnDrawerOpenedChanged(NavigationDrawer drawer, + AvaloniaPropertyChangedEventArgs args) + { + drawer.UpdatePseudoClasses(); + drawer.UpdateContentMargin(); + } + + // ReSharper disable once InconsistentNaming + private Border? PART_Scrim; + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + if (e.NameScope.Find("PART_Scrim") is Border border) + { + PART_Scrim = border; + + PART_Scrim.PointerPressed += PART_Scrim_Pressed; + } + + base.OnApplyTemplate(e); + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + if (PART_Scrim != null) + PART_Scrim.PointerPressed += PART_Scrim_Pressed; + + base.OnAttachedToVisualTree(e); + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + if (PART_Scrim != null) + PART_Scrim.PointerPressed -= PART_Scrim_Pressed; + + base.OnDetachedFromVisualTree(e); + } + + private void UpdateDesktopExpand(double w) + { + if (LeftDrawerExpandThresholdWidth.HasValue) + { + var status = w > LeftDrawerExpandThresholdWidth.Value; + _isLeftDrawerDesktopExpanded = status; + + if (Classes.Contains(":left-expand") != status) + { + LeftDrawerOpened = status; + } + + PseudoClasses.Set(":left-expand", status); + } + else + { + _isLeftDrawerDesktopExpanded = false; + } + + if (RightDrawerExpandThresholdWidth.HasValue) + { + var status = w > RightDrawerExpandThresholdWidth.Value; + _isRightDrawerDesktopExpanded = status; + + if (Classes.Contains(":right-expand") != status) + { + RightDrawerOpened = status; + } + + PseudoClasses.Set(":right-expand", status); + } + else + { + _isRightDrawerDesktopExpanded = false; + } + } + + private void UpdateContentMargin() + { + var left = _isLeftDrawerDesktopExpanded && LeftDrawerOpened ? LeftDrawerWidth : 0; + var right = _isRightDrawerDesktopExpanded && RightDrawerOpened ? RightDrawerWidth : 0; + + ContentMargin = new Thickness(left, 0, right, 0); + } + + private void PART_Scrim_Pressed(object sender, RoutedEventArgs e) + { + LeftDrawerOpened = false; + RightDrawerOpened = false; + } + + private void UpdatePseudoClasses() + { + var open = LeftDrawerOpened || RightDrawerOpened; + PseudoClasses.Set(":open", open); + PseudoClasses.Set(":closed", !open); + PseudoClasses.Set(":left", LeftDrawerOpened); + PseudoClasses.Set(":right", RightDrawerOpened); + } + } +} \ No newline at end of file diff --git a/Material.Styles/Controls/Rotator.cs b/Material.Styles/Controls/Rotator.cs index 637bc3f..ba1be02 100644 --- a/Material.Styles/Controls/Rotator.cs +++ b/Material.Styles/Controls/Rotator.cs @@ -27,18 +27,19 @@ namespace Material.Styles.Controls set { if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value), "MinimumSpeed should not less than zero. You can set it as zero, if you wish your rotator keep running."); + throw new ArgumentOutOfRangeException(nameof(value), + "MinimumSpeed should not less than zero. You can set it as zero, if you wish your rotator keep running."); _minimumSpeed = value; } } - + private bool _running; private double _speed = 0.4; private double _rotateDegree; - + private readonly RenderLoopClock _loopTask; private TimeSpan _prev; @@ -64,7 +65,7 @@ namespace Material.Styles.Controls { if (rotator.IsEffectivelyVisible == false || rotator.IsEffectivelyEnabled == false) return; - + rotator._speed = v; OnSpeedChanged(rotator, v); }); @@ -74,7 +75,7 @@ namespace Material.Styles.Controls { if (IsEffectivelyVisible == false || IsEffectivelyEnabled == false) return; - + var delta = renderTime - _prev; _rotateDegree += _speed * delta.TotalMilliseconds; _prev = renderTime; @@ -84,7 +85,7 @@ namespace Material.Styles.Controls RenderTransform = new RotateTransform(_rotateDegree); } - + private static void OnSpeedChanged(Rotator rotator, double d) { // We should stop rotator if speed is lower than minimum speed diff --git a/Material.Styles/Controls/Scroller.cs b/Material.Styles/Controls/Scroller.cs index 232bc85..c5d3c94 100644 --- a/Material.Styles/Controls/Scroller.cs +++ b/Material.Styles/Controls/Scroller.cs @@ -188,7 +188,7 @@ namespace Material.Styles.Controls RoutedEvent.Register( nameof(ScrollChanged), RoutingStrategies.Bubble); - + /// /// Defines the property. /// @@ -196,7 +196,7 @@ namespace Material.Styles.Controls AvaloniaProperty.RegisterDirect( nameof(CanScrollLeft), o => o.CanScrollLeft); - + /// /// Defines the property. /// @@ -227,8 +227,10 @@ namespace Material.Styles.Controls /// static Scroller() { - HorizontalScrollBarVisibilityProperty.Changed.AddClassHandler((x, e) => x.ScrollBarVisibilityChanged(e)); - VerticalScrollBarVisibilityProperty.Changed.AddClassHandler((x, e) => x.ScrollBarVisibilityChanged(e)); + HorizontalScrollBarVisibilityProperty.Changed.AddClassHandler((x, e) => + x.ScrollBarVisibilityChanged(e)); + VerticalScrollBarVisibilityProperty.Changed.AddClassHandler((x, e) => + x.ScrollBarVisibilityChanged(e)); } /// @@ -253,10 +255,7 @@ namespace Material.Styles.Controls /// public Size Extent { - get - { - return _extent; - } + get { return _extent; } private set { @@ -272,10 +271,7 @@ namespace Material.Styles.Controls /// public Vector Offset { - get - { - return _offset; - } + get { return _offset; } set { @@ -291,10 +287,7 @@ namespace Material.Styles.Controls /// public Size Viewport { - get - { - return _viewport; - } + get { return _viewport; } private set { @@ -435,8 +428,8 @@ namespace Material.Styles.Controls get => GetValue(AllowAutoHideProperty); set => SetValue(AllowAutoHideProperty, value); } - - + + /// /// /// @@ -445,7 +438,7 @@ namespace Material.Styles.Controls get => _canScrollLeft; private set => _canScrollLeft = value; } - + /// /// /// @@ -454,7 +447,7 @@ namespace Material.Styles.Controls get => _canScrollRight; private set => _canScrollRight = value; } - + /// /// Scrolls the content up one line. @@ -487,7 +480,7 @@ namespace Material.Styles.Controls { Offset += new Vector(_smallChange.Width, 0); } - + /// /// Scrolls the content upward by half page. /// @@ -519,7 +512,7 @@ namespace Material.Styles.Controls { HorizontalScrollBarValue = Math.Min(_offset.X + _viewport.Width / 3.0, HorizontalScrollBarMaximum); } - + /// /// Scrolls the content upward by half page. /// @@ -844,7 +837,8 @@ namespace Material.Styles.Controls var offsetDelta = Offset - _oldOffset; var viewportDelta = new Vector(Viewport.Width - _oldViewport.Width, Viewport.Height - _oldViewport.Height); - if (!extentDelta.NearlyEquals(default) || !offsetDelta.NearlyEquals(default) || !viewportDelta.NearlyEquals(default)) + if (!extentDelta.NearlyEquals(default) || !offsetDelta.NearlyEquals(default) || + !viewportDelta.NearlyEquals(default)) { var e = new ScrollChangedEventArgs(extentDelta, offsetDelta, viewportDelta); OnScrollChanged(e); diff --git a/Material.Styles/Controls/ScrollerControlPresenter.cs b/Material.Styles/Controls/ScrollerControlPresenter.cs index 69d864d..1063939 100644 --- a/Material.Styles/Controls/ScrollerControlPresenter.cs +++ b/Material.Styles/Controls/ScrollerControlPresenter.cs @@ -1,16 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reactive.Disposables; -using System.Reactive.Linq; +#nullable enable + +using System; using Avalonia; -using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Input; -using Avalonia.VisualTree; - -#nullable enable namespace Material.Styles.Controls { @@ -39,7 +33,7 @@ namespace Material.Styles.Controls Offset = new Vector(x, y); e.Handled = true; } - + /*if (Extent.Height > Viewport.Height || Extent.Width > Viewport.Width) { var scrollable = Child as ILogicalScrollable; @@ -69,4 +63,4 @@ namespace Material.Styles.Controls }*/ } } -} +} \ No newline at end of file diff --git a/Material.Styles/SideSheet.xaml.cs b/Material.Styles/Controls/SideSheet.cs similarity index 92% rename from Material.Styles/SideSheet.xaml.cs rename to Material.Styles/Controls/SideSheet.cs index b3c3cda..5d70107 100644 --- a/Material.Styles/SideSheet.xaml.cs +++ b/Material.Styles/Controls/SideSheet.cs @@ -1,23 +1,23 @@ 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")] public class SideSheet : ContentControl - { + { // Avalonia properties - + public static readonly StyledProperty SideSheetContentProperty = AvaloniaProperty.Register(nameof(SideSheetContent)); - + public static readonly StyledProperty SideSheetContentTemplateProperty = AvaloniaProperty.Register(nameof(SideSheetContentTemplate)); @@ -29,21 +29,21 @@ namespace Material.Styles public static readonly StyledProperty SideSheetDirectionProperty = AvaloniaProperty.Register(nameof(SideSheetDirection)); - + public static readonly StyledProperty SideSheetHeaderProperty = AvaloniaProperty.Register(nameof(SideSheetHeader)); - + public static readonly StyledProperty SideSheetHeaderTemplateProperty = AvaloniaProperty.Register(nameof(SideSheetHeaderTemplate)); - + public static readonly StyledProperty SideSheetPaddingProperty = AvaloniaProperty.Register(nameof(SideSheetPadding)); - + public static readonly StyledProperty SideSheetCanCloseProperty = AvaloniaProperty.Register(nameof(SideSheetCanClose), true); // CLR properties - + /// /// Hide or show the default close button /// @@ -52,16 +52,16 @@ namespace Material.Styles get => GetValue(SideSheetCanCloseProperty); set => SetValue(SideSheetCanCloseProperty, value); } - + /// /// Gets or sets the padding in the around the /// public Thickness SideSheetPadding { - get { return GetValue(SideSheetPaddingProperty); } - set { SetValue(SideSheetPaddingProperty, value); } + get => GetValue(SideSheetPaddingProperty); + set => SetValue(SideSheetPaddingProperty, value); } - + /// /// Gets or sets the content of the header to display. /// @@ -71,7 +71,7 @@ namespace Material.Styles get => GetValue(SideSheetHeaderProperty); set => SetValue(SideSheetHeaderProperty, value); } - + /// /// Gets or sets the data template used to display the header of the control. /// @@ -80,7 +80,7 @@ namespace Material.Styles get => GetValue(SideSheetHeaderTemplateProperty); set => SetValue(SideSheetHeaderTemplateProperty, value); } - + /// /// Gets or sets the content to display. /// @@ -111,7 +111,7 @@ namespace Material.Styles get => GetValue(SideSheetWidthProperty); set => SetValue(SideSheetWidthProperty, value); } - + public HorizontalDirection SideSheetDirection { get => GetValue(SideSheetDirectionProperty); @@ -135,7 +135,8 @@ namespace Material.Styles } // Controls - + + // ReSharper disable once InconsistentNaming private Border? PART_Scrim; protected override void OnApplyTemplate(TemplateAppliedEventArgs e) @@ -143,34 +144,34 @@ namespace Material.Styles if (e.NameScope.Find("PART_Scrim") is Border border) { PART_Scrim = border; - + PART_Scrim.PointerPressed += PART_Scrim_Pressed; } if (e.NameScope.Find("PART_CloseButton") is Button button) { - button.Click += (sender, args) => SideSheetOpened = false; + button.Click += (_, _) => SideSheetOpened = false; } - + base.OnApplyTemplate(e); } - + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { - if(PART_Scrim != null) + if (PART_Scrim != null) PART_Scrim.PointerPressed += PART_Scrim_Pressed; - + base.OnAttachedToVisualTree(e); } protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) { - if(PART_Scrim != null) + if (PART_Scrim != null) PART_Scrim.PointerPressed -= PART_Scrim_Pressed; - + base.OnDetachedFromVisualTree(e); } - + private void PART_Scrim_Pressed(object sender, RoutedEventArgs e) { SideSheetOpened = false; @@ -187,4 +188,4 @@ namespace Material.Styles PseudoClasses.Set(":right", direction == HorizontalDirection.Right); } } -} +} \ No newline at end of file diff --git a/Material.Styles/SnackbarHost.xaml.cs b/Material.Styles/Controls/SnackbarHost.cs similarity index 76% rename from Material.Styles/SnackbarHost.xaml.cs rename to Material.Styles/Controls/SnackbarHost.cs index 3166f88..c164106 100644 --- a/Material.Styles/SnackbarHost.xaml.cs +++ b/Material.Styles/Controls/SnackbarHost.cs @@ -5,21 +5,20 @@ 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; - + /// /// Get the name of host. The name of host can be set only one time. /// @@ -32,22 +31,20 @@ namespace Material.Styles { SetValue(HostNameProperty, value); - if (_snackbarHostDictionary.ContainsValue(this)) - { - KeyValuePair? target = null; - foreach (var host in _snackbarHostDictionary) - { - if (ReferenceEquals(host.Value, this)) - { - target = host; - break; - } - } + if (!SnackbarHostDictionary.ContainsValue(this)) + return; - if (target.HasValue) - { - _snackbarHostDictionary.Remove(target.Value.Key); - } + KeyValuePair? target = null; + foreach (var host in SnackbarHostDictionary + .Where(host => ReferenceEquals(host.Value, this))) + { + target = host; + break; + } + + if (target.HasValue) + { + SnackbarHostDictionary.Remove(target.Value.Key); } } else @@ -65,7 +62,8 @@ namespace Material.Styles } public static readonly StyledProperty SnackbarHorizontalAlignmentProperty = - AvaloniaProperty.Register(nameof(SnackbarHorizontalAlignment), HorizontalAlignment.Left); + AvaloniaProperty.Register(nameof(SnackbarHorizontalAlignment), + HorizontalAlignment.Left); public VerticalAlignment SnackbarVerticalAlignment { @@ -74,14 +72,15 @@ namespace Material.Styles } public static readonly StyledProperty SnackbarVerticalAlignmentProperty = - AvaloniaProperty.Register(nameof(SnackbarVerticalAlignment), VerticalAlignment.Bottom); + AvaloniaProperty.Register(nameof(SnackbarVerticalAlignment), + VerticalAlignment.Bottom); static SnackbarHost() { //_snackbarHosts = new HashSet(); - _snackbarHostDictionary = new Dictionary(); + SnackbarHostDictionary = new Dictionary(); } - + public SnackbarHost() { // Initialize model collection @@ -90,11 +89,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 +101,7 @@ namespace Material.Styles if (name is null) throw new ArgumentNullException(nameof(name)); - var result = _snackbarHostDictionary[name]; + var result = SnackbarHostDictionary[name]; return result; } @@ -139,9 +138,9 @@ namespace Material.Styles { void OnExpired(object sender, ElapsedEventArgs args) { - if (sender is not Timer timer) + if (sender is not Timer timer) return; - + // Remove timer. timer.Stop(); timer.Elapsed -= OnExpired; @@ -164,42 +163,38 @@ 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(); - + var host = GetHost(targetHost); if (host is null) - throw new ArgumentNullException(nameof(targetHost), $"The target host named \"{targetHost}\" is not exist."); + throw new ArgumentNullException(nameof(targetHost), + $"The target host named \"{targetHost}\" is not exist."); - Dispatcher.UIThread.Post(delegate - { - host.SnackbarModels.Remove(model); - }, priority); + Dispatcher.UIThread.Post(delegate { host.SnackbarModels.Remove(model); }, priority); } private static void OnSnackbarDurationExpired(SnackbarHost host, SnackbarModel model) { - Dispatcher.UIThread.Post(delegate - { - host.SnackbarModels.Remove(model); - }); + Dispatcher.UIThread.Post(delegate { host.SnackbarModels.Remove(model); }); } 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); - - base.OnDetachedFromLogicalTree(e); + SnackbarHostDictionary.Remove(HostName); + + base.OnDetachedFromVisualTree(e); } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) @@ -208,8 +203,8 @@ namespace Material.Styles if (HostName is null) throw new ArgumentNullException(nameof(HostName), "The name of SnackbarHost is null. Please define it."); - + base.OnApplyTemplate(e); } } -} +} \ No newline at end of file diff --git a/Material.Styles/Controls/TickBar.cs b/Material.Styles/Controls/TickBar.cs new file mode 100644 index 0000000..039f756 --- /dev/null +++ b/Material.Styles/Controls/TickBar.cs @@ -0,0 +1,377 @@ +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(); + } + } + } +} \ No newline at end of file diff --git a/Material.Styles/Converters/AutoCorrectPositionConverter.cs b/Material.Styles/Converters/AutoCorrectPositionConverter.cs index bab5078..168cfa9 100644 --- a/Material.Styles/Converters/AutoCorrectPositionConverter.cs +++ b/Material.Styles/Converters/AutoCorrectPositionConverter.cs @@ -2,60 +2,67 @@ 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 { - public class AutoCorrectPositionConverter : IValueConverter + public class AutoCorrectPositionConverter : IMultiValueConverter { + public static readonly Transform Empty = new MatrixTransform(); + public static double DefaultOffsetY = 0; - private static double GetOffLeft(Rect bounds, double offsetX) => offsetX; + private static double GetOffLeft(double offsetX) => offsetX; - private static double GetOffRight(Rect bounds, double windowW, double offsetX) => offsetX + (bounds.Width) - windowW; - - private static Vector GetTranslate(TransformedBounds bounds) + private static double GetOffRight(Rect bounds, double clipW, double offsetX) { - return new Vector(bounds.Transform.M31, bounds.Transform.M32); + var r = offsetX + bounds.Width; + + return Math.Max(0, r - clipW); } - private Vector _prevCorrect = Vector.One; - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + private static Vector GetTranslate(Matrix m) + { + return Matrix.TryDecomposeTransform(m, out var decomposed) ? + decomposed.Translate : Vector.Zero; + } + + public object Convert(IList values, Type targetType, object? parameter, CultureInfo culture) { - var transformedBounds = value as TransformedBounds?; - double offsetX = 0; - if (transformedBounds.HasValue) - { - var bounds = transformedBounds.Value; + + if (values.Count <= 1 || values.Count > 2) + return Empty; + + if (values[1] is not Rect clip) + return Empty; + + if (values[0] is not TransformedBounds postTransformations) + return Empty; + + var t = postTransformations.Transform; + var b = postTransformations.Bounds; + var c = 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(translate.X); + var right = GetOffRight(b, c.Width, translate.X); - if (left < 0) - { - offsetX = -left; - //_prevCorrect = new Vector(offsetX, DefaultOffsetY); - } - else if (right > 0) - { - offsetX = -right; - // _prevCorrect = new Vector(offsetX, DefaultOffsetY); - } + if (left < 0) + { + offsetX = -left; + //_prevCorrect = new Vector(offsetX, DefaultOffsetY); } + else if (right > 0) + { + offsetX = -right; + // _prevCorrect = new Vector(offsetX, DefaultOffsetY); + } + return new TranslateTransform(offsetX, DefaultOffsetY); } - - 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/BrushRoundConverter.cs b/Material.Styles/Converters/BrushRoundConverter.cs index 7f178c3..9761878 100644 --- a/Material.Styles/Converters/BrushRoundConverter.cs +++ b/Material.Styles/Converters/BrushRoundConverter.cs @@ -3,14 +3,17 @@ using System.Globalization; using Avalonia.Data.Converters; using Avalonia.Media; -namespace Material.Styles.Converters { - public class BrushRoundConverter : IValueConverter { +namespace Material.Styles.Converters +{ + public class BrushRoundConverter : IValueConverter + { public static readonly IValueConverter Instance = new BrushRoundConverter(); public Brush HighValue { get; set; } = new SolidColorBrush(Brushes.White.Color); public Brush LowValue { get; set; } = new SolidColorBrush(Brushes.Black.Color); - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { if (!(value is SolidColorBrush solidColorBrush)) return null; var color = solidColorBrush.Color; @@ -20,7 +23,8 @@ namespace Material.Styles.Converters { return brightness < 123 ? LowValue : HighValue; } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { throw new NotImplementedException(); } } diff --git a/Material.Styles/Converters/DatePickerTextConverter.cs b/Material.Styles/Converters/DatePickerTextConverter.cs index 0a04564..6856180 100644 --- a/Material.Styles/Converters/DatePickerTextConverter.cs +++ b/Material.Styles/Converters/DatePickerTextConverter.cs @@ -5,16 +5,24 @@ using Avalonia; using Avalonia.Data; using Avalonia.Data.Converters; -namespace Material.Styles.Converters { - public class DatePickerTextConverter : IMultiValueConverter { +namespace Material.Styles.Converters +{ + public class DatePickerTextConverter : IMultiValueConverter + { public static DatePickerTextConverter Instance { get; } = new DatePickerTextConverter(); - public object Convert(IList values, Type targetType, object parameter, CultureInfo culture) { + + public object Convert(IList values, Type targetType, object parameter, CultureInfo culture) + { // ReSharper disable once ConditionIsAlwaysTrueOrFalse // ReSharper disable once HeuristicUnreachableCode - try { - return values[0] is UnsetValueType || values[0] == null ? "Not selected" : ((DateTimeOffset)values[0]).ToString((string)values[1]); + try + { + return values[0] is UnsetValueType || values[0] == null + ? "Not selected" + : ((DateTimeOffset) values[0]).ToString((string) values[1]); } - catch (Exception) { + catch (Exception) + { return BindingOperations.DoNothing; } } diff --git a/Material.Styles/Converters/DateTimeToOffsetConverter.cs b/Material.Styles/Converters/DateTimeToOffsetConverter.cs index ad3a777..7604bde 100644 --- a/Material.Styles/Converters/DateTimeToOffsetConverter.cs +++ b/Material.Styles/Converters/DateTimeToOffsetConverter.cs @@ -2,23 +2,29 @@ using System; using System.Globalization; using Avalonia.Data.Converters; -namespace Material.Styles.Converters { - public class DateTimeToOffsetConverter : IValueConverter { +namespace Material.Styles.Converters +{ + public class DateTimeToOffsetConverter : IValueConverter + { public static DateTimeToOffsetConverter Instance { get; } = new DateTimeToOffsetConverter(); - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - if (value is DateTimeOffset offset) { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is DateTimeOffset offset) + { return offset.Date; } - + return value; } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - if (value is DateTime dateTime) { + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is DateTime dateTime) + { return new DateTimeOffset(dateTime); } - + return value; } } diff --git a/Material.Styles/Converters/GetPlatformHotkeyConfigServiceConverter.cs b/Material.Styles/Converters/GetPlatformHotkeyConfigServiceConverter.cs index e44b941..fe91569 100644 --- a/Material.Styles/Converters/GetPlatformHotkeyConfigServiceConverter.cs +++ b/Material.Styles/Converters/GetPlatformHotkeyConfigServiceConverter.cs @@ -15,7 +15,7 @@ namespace Material.Styles.Converters { _config = AvaloniaLocator.Current.GetService(); } - + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { if (parameter is not string kind) diff --git a/Material.Styles/Converters/InverseBooleanValueConverter.cs b/Material.Styles/Converters/InverseBooleanValueConverter.cs index 1431d40..394d8f8 100644 --- a/Material.Styles/Converters/InverseBooleanValueConverter.cs +++ b/Material.Styles/Converters/InverseBooleanValueConverter.cs @@ -2,15 +2,19 @@ using System; using System.Globalization; using Avalonia.Data.Converters; -namespace Material.Styles { - internal class InverseBooleanValueConverter : IValueConverter { +namespace Material.Styles +{ + internal class InverseBooleanValueConverter : IValueConverter + { public bool Default { get; set; } - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { return value is bool b ? !b : Default; } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { return value is bool b ? !b : !Default; } } diff --git a/Material.Styles/Converters/MarginCreator.cs b/Material.Styles/Converters/MarginCreator.cs index 25f240f..7aaa05a 100644 --- a/Material.Styles/Converters/MarginCreator.cs +++ b/Material.Styles/Converters/MarginCreator.cs @@ -1,9 +1,8 @@ -using Avalonia; -using Avalonia.Data.Converters; -using System; +using System; using System.Collections.Generic; using System.Globalization; -using System.Text; +using Avalonia; +using Avalonia.Data.Converters; namespace Material.Styles.Converters { @@ -16,17 +15,18 @@ namespace Material.Styles.Converters public object Convert(IList values, Type targetType, object parameter, CultureInfo culture) { - if ((values[0] is double left)) + if ((values[0] is double left)) return createMargin(left: -left + Offset); - if ((values[1] is double up)) + if ((values[1] is double up)) return createMargin(up: -up + Offset); - if ((values[2] is double right)) + if ((values[2] is double right)) return createMargin(right: -right + Offset); - if ((values[3] is double down)) + if ((values[3] is double down)) return createMargin(down: -down + Offset); return createMargin(); } - public static Thickness createMargin(double left = 0, double up = 0, double right = 0, double down = 0) => new Thickness(left, up, right, down); + public static Thickness createMargin(double left = 0, double up = 0, double right = 0, double down = 0) => + new Thickness(left, up, right, down); } -} +} \ No newline at end of file diff --git a/Material.Styles/Converters/MarginValueMultiplyConverter.cs b/Material.Styles/Converters/MarginValueMultiplyConverter.cs index 0e9f0c6..4bfc213 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), - return Thickness.Parse("0"); + // 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 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..4fbc9ba --- /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/MarginMultiplyParameter.cs b/Material.Styles/Converters/Parameters/MarginMultiplyParameter.cs index ddc994d..f9512a1 100644 --- a/Material.Styles/Converters/Parameters/MarginMultiplyParameter.cs +++ b/Material.Styles/Converters/Parameters/MarginMultiplyParameter.cs @@ -3,7 +3,7 @@ public class MarginMultiplyParameter { public static MarginMultiplyParameter Default { get; } = new(); - + public double LeftMultiplier { get; set; } public double TopMultiplier { get; set; } public double RightMultiplier { get; set; } 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..a210460 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..9eea998 --- /dev/null +++ b/Material.Styles/Converters/RectHollowClipConverter.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using Avalonia; +using Avalonia.Data.Converters; +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 diff --git a/Material.Styles/Converters/WrapContentIntoContentPresenterConverter.cs b/Material.Styles/Converters/WrapContentIntoContentPresenterConverter.cs index d4cf607..d768bb1 100644 --- a/Material.Styles/Converters/WrapContentIntoContentPresenterConverter.cs +++ b/Material.Styles/Converters/WrapContentIntoContentPresenterConverter.cs @@ -4,16 +4,20 @@ using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.Data.Converters; -namespace Material.Styles.Converters { - internal class WrapContentIntoContentPresenterConverter : IValueConverter { - public static WrapContentIntoContentPresenterConverter Instance { get; } = new WrapContentIntoContentPresenterConverter(); - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - return value is IControl ? value : new ContentPresenter() { Content = value }; +namespace Material.Styles.Converters +{ + internal class WrapContentIntoContentPresenterConverter : IValueConverter + { + public static WrapContentIntoContentPresenterConverter Instance { get; } = + new WrapContentIntoContentPresenterConverter(); + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value is IControl ? value : new ContentPresenter() {Content = value}; } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { throw new NotSupportedException(); } } 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..0e9d492 100644 --- a/Material.Styles/DatePicker.xaml +++ b/Material.Styles/DatePicker.xaml @@ -2,74 +2,92 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 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 deleted file mode 100644 index 4e5cb72..0000000 --- a/Material.Styles/DialogHost.axaml +++ /dev/null @@ -1,17 +0,0 @@ - - - diff --git a/Material.Styles/Enums/HorizontalDirection.cs b/Material.Styles/Enums/HorizontalDirection.cs index ecafc0d..b5fa543 100644 --- a/Material.Styles/Enums/HorizontalDirection.cs +++ b/Material.Styles/Enums/HorizontalDirection.cs @@ -2,6 +2,7 @@ { public enum HorizontalDirection { - Left, Right + Left, + Right } } \ No newline at end of file diff --git a/Material.Styles/Expander.xaml b/Material.Styles/Expander.xaml index 412d4e5..0f5bb45 100644 --- a/Material.Styles/Expander.xaml +++ b/Material.Styles/Expander.xaml @@ -1,116 +1,150 @@ - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + @@ -156,19 +183,20 @@ + + + + + \ No newline at end of file diff --git a/Material.Styles/FloatingButton.xaml b/Material.Styles/FloatingButton.xaml index b6231e1..1ff1582 100644 --- a/Material.Styles/FloatingButton.xaml +++ b/Material.Styles/FloatingButton.xaml @@ -1,10 +1,10 @@ + xmlns:system="clr-namespace:System;assembly=netstandard" + xmlns:controls="clr-namespace:Material.Styles.Controls"> @@ -15,7 +15,7 @@ - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/Material.Styles/FlyoutPresenter.axaml b/Material.Styles/FlyoutPresenter.axaml index 6122432..8c9c68a 100644 --- a/Material.Styles/FlyoutPresenter.axaml +++ b/Material.Styles/FlyoutPresenter.axaml @@ -1,6 +1,6 @@ + xmlns:controls="clr-namespace:Material.Styles.Controls"> 456 758 @@ -20,7 +20,7 @@ - @@ -36,7 +36,7 @@ HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" /> - + diff --git a/Material.Styles/Internal/IconsDataSet.cs b/Material.Styles/Internal/IconsDataSet.cs index 4909dd1..b4f0394 100644 --- a/Material.Styles/Internal/IconsDataSet.cs +++ b/Material.Styles/Internal/IconsDataSet.cs @@ -6,20 +6,45 @@ namespace Material.Styles.Internal { internal const string UnknownIconData = "M11,15H13V17H11V15M11,7H13V13H11V7M12,2C6.47,2 2,6.5 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20Z"; + internal static IDictionary CreateDataSet() => new Dictionary { - {"Calendar", "M19,19H5V8H19M16,1V3H8V1H6V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3H18V1M17,12H12V17H17V12Z"}, + { + "Calendar", + "M19,19H5V8H19M16,1V3H8V1H6V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3H18V1M17,12H12V17H17V12Z" + }, {"ChevronLeft", "M15.41,16.58L10.83,12L15.41,7.41L14,6L8,12L14,18L15.41,16.58Z"}, {"ChevronRight", "M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z"}, - {"EyeOutline", "M12,9A3,3 0 0,1 15,12A3,3 0 0,1 12,15A3,3 0 0,1 9,12A3,3 0 0,1 12,9M12,4.5C17,4.5 21.27,7.61 23,12C21.27,16.39 17,19.5 12,19.5C7,19.5 2.73,16.39 1,12C2.73,7.61 7,4.5 12,4.5M3.18,12C4.83,15.36 8.24,17.5 12,17.5C15.76,17.5 19.17,15.36 20.82,12C19.17,8.64 15.76,6.5 12,6.5C8.24,6.5 4.83,8.64 3.18,12Z"}, - {"EyeOffOutline", "M2,5.27L3.28,4L20,20.72L18.73,22L15.65,18.92C14.5,19.3 13.28,19.5 12,19.5C7,19.5 2.73,16.39 1,12C1.69,10.24 2.79,8.69 4.19,7.46L2,5.27M12,9A3,3 0 0,1 15,12C15,12.35 14.94,12.69 14.83,13L11,9.17C11.31,9.06 11.65,9 12,9M12,4.5C17,4.5 21.27,7.61 23,12C22.18,14.08 20.79,15.88 19,17.19L17.58,15.76C18.94,14.82 20.06,13.54 20.82,12C19.17,8.64 15.76,6.5 12,6.5C10.91,6.5 9.84,6.68 8.84,7L7.3,5.47C8.74,4.85 10.33,4.5 12,4.5M3.18,12C4.83,15.36 8.24,17.5 12,17.5C12.69,17.5 13.37,17.43 14,17.29L11.72,15C10.29,14.85 9.15,13.71 9,12.28L5.6,8.87C4.61,9.72 3.78,10.78 3.18,12Z"}, - {"Close", "M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"}, - + { + "EyeOutline", + "M12,9A3,3 0 0,1 15,12A3,3 0 0,1 12,15A3,3 0 0,1 9,12A3,3 0 0,1 12,9M12,4.5C17,4.5 21.27,7.61 23,12C21.27,16.39 17,19.5 12,19.5C7,19.5 2.73,16.39 1,12C2.73,7.61 7,4.5 12,4.5M3.18,12C4.83,15.36 8.24,17.5 12,17.5C15.76,17.5 19.17,15.36 20.82,12C19.17,8.64 15.76,6.5 12,6.5C8.24,6.5 4.83,8.64 3.18,12Z" + }, + { + "EyeOffOutline", + "M2,5.27L3.28,4L20,20.72L18.73,22L15.65,18.92C14.5,19.3 13.28,19.5 12,19.5C7,19.5 2.73,16.39 1,12C1.69,10.24 2.79,8.69 4.19,7.46L2,5.27M12,9A3,3 0 0,1 15,12C15,12.35 14.94,12.69 14.83,13L11,9.17C11.31,9.06 11.65,9 12,9M12,4.5C17,4.5 21.27,7.61 23,12C22.18,14.08 20.79,15.88 19,17.19L17.58,15.76C18.94,14.82 20.06,13.54 20.82,12C19.17,8.64 15.76,6.5 12,6.5C10.91,6.5 9.84,6.68 8.84,7L7.3,5.47C8.74,4.85 10.33,4.5 12,4.5M3.18,12C4.83,15.36 8.24,17.5 12,17.5C12.69,17.5 13.37,17.43 14,17.29L11.72,15C10.29,14.85 9.15,13.71 9,12.28L5.6,8.87C4.61,9.72 3.78,10.78 3.18,12Z" + }, + { + "Close", + "M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" + }, + // Context menu -- For TextBox context menu use - {"MaterialContentCut", "M19 3L13 9L15 11L22 4V3M12 12.5A0.5 0.5 0 0 1 11.5 12A0.5 0.5 0 0 1 12 11.5A0.5 0.5 0 0 1 12.5 12A0.5 0.5 0 0 1 12 12.5M6 20A2 2 0 0 1 4 18C4 16.89 4.9 16 6 16A2 2 0 0 1 8 18C8 19.11 7.1 20 6 20M6 8A2 2 0 0 1 4 6C4 4.89 4.9 4 6 4A2 2 0 0 1 8 6C8 7.11 7.1 8 6 8M9.64 7.64C9.87 7.14 10 6.59 10 6A4 4 0 0 0 6 2A4 4 0 0 0 2 6A4 4 0 0 0 6 10C6.59 10 7.14 9.87 7.64 9.64L10 12L7.64 14.36C7.14 14.13 6.59 14 6 14A4 4 0 0 0 2 18A4 4 0 0 0 6 22A4 4 0 0 0 10 18C10 17.41 9.87 16.86 9.64 16.36L12 14L19 21H22V20L9.64 7.64Z"}, - {"MaterialContentCopy", "M19 21H8V7H19M19 5H8A2 2 0 0 0 6 7V21A2 2 0 0 0 8 23H19A2 2 0 0 0 21 21V7A2 2 0 0 0 19 5M16 1H4A2 2 0 0 0 2 3V17H4V3H16V1Z"}, - {"MaterialContentPaste", "M19 20H5V4H7V7H17V4H19M12 2A1 1 0 0 1 13 3A1 1 0 0 1 12 4A1 1 0 0 1 11 3A1 1 0 0 1 12 2M19 2H14.82C14.4 0.84 13.3 0 12 0C10.7 0 9.6 0.84 9.18 2H5A2 2 0 0 0 3 4V20A2 2 0 0 0 5 22H19A2 2 0 0 0 21 20V4A2 2 0 0 0 19 2Z"}, - {"MaterialSelectAll", "M9 9H15V15H9M7 17H17V7H7M15 5H17V3H15M15 21H17V19H15M19 17H21V15H19M19 9H21V7H19M19 21A2 2 0 0 0 21 19H19M19 13H21V11H19M11 21H13V19H11M9 3H7V5H9M3 17H5V15H3M5 21V19H3A2 2 0 0 0 5 21M19 3V5H21A2 2 0 0 0 19 3M13 3H11V5H13M3 9H5V7H3M7 21H9V19H7M3 13H5V11H3M3 5H5V3A2 2 0 0 0 3 5Z"}, + { + "MaterialContentCut", + "M19 3L13 9L15 11L22 4V3M12 12.5A0.5 0.5 0 0 1 11.5 12A0.5 0.5 0 0 1 12 11.5A0.5 0.5 0 0 1 12.5 12A0.5 0.5 0 0 1 12 12.5M6 20A2 2 0 0 1 4 18C4 16.89 4.9 16 6 16A2 2 0 0 1 8 18C8 19.11 7.1 20 6 20M6 8A2 2 0 0 1 4 6C4 4.89 4.9 4 6 4A2 2 0 0 1 8 6C8 7.11 7.1 8 6 8M9.64 7.64C9.87 7.14 10 6.59 10 6A4 4 0 0 0 6 2A4 4 0 0 0 2 6A4 4 0 0 0 6 10C6.59 10 7.14 9.87 7.64 9.64L10 12L7.64 14.36C7.14 14.13 6.59 14 6 14A4 4 0 0 0 2 18A4 4 0 0 0 6 22A4 4 0 0 0 10 18C10 17.41 9.87 16.86 9.64 16.36L12 14L19 21H22V20L9.64 7.64Z" + }, + { + "MaterialContentCopy", + "M19 21H8V7H19M19 5H8A2 2 0 0 0 6 7V21A2 2 0 0 0 8 23H19A2 2 0 0 0 21 21V7A2 2 0 0 0 19 5M16 1H4A2 2 0 0 0 2 3V17H4V3H16V1Z" + }, + { + "MaterialContentPaste", + "M19 20H5V4H7V7H17V4H19M12 2A1 1 0 0 1 13 3A1 1 0 0 1 12 4A1 1 0 0 1 11 3A1 1 0 0 1 12 2M19 2H14.82C14.4 0.84 13.3 0 12 0C10.7 0 9.6 0.84 9.18 2H5A2 2 0 0 0 3 4V20A2 2 0 0 0 5 22H19A2 2 0 0 0 21 20V4A2 2 0 0 0 19 2Z" + }, + { + "MaterialSelectAll", + "M9 9H15V15H9M7 17H17V7H7M15 5H17V3H15M15 21H17V19H15M19 17H21V15H19M19 9H21V7H19M19 21A2 2 0 0 0 21 19H19M19 13H21V11H19M11 21H13V19H11M9 3H7V5H9M3 17H5V15H3M5 21V19H3A2 2 0 0 0 5 21M19 3V5H21A2 2 0 0 0 19 3M13 3H11V5H13M3 9H5V7H3M7 21H9V19H7M3 13H5V11H3M3 5H5V3A2 2 0 0 0 3 5Z" + }, }; } } \ No newline at end of file diff --git a/Material.Styles/ListBoxItem.xaml b/Material.Styles/ListBoxItem.xaml index 0691e7a..711bebf 100644 --- a/Material.Styles/ListBoxItem.xaml +++ b/Material.Styles/ListBoxItem.xaml @@ -1,72 +1,76 @@ - - - - - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/Material.Styles/Material.Styles.csproj b/Material.Styles/Material.Styles.csproj index 42b3145..eb7ac45 100644 --- a/Material.Styles/Material.Styles.csproj +++ b/Material.Styles/Material.Styles.csproj @@ -55,8 +55,7 @@ - - + diff --git a/Material.Styles/MaterialToolKit.xaml b/Material.Styles/MaterialToolKit.xaml index 6b082b7..b87e4dc 100644 --- a/Material.Styles/MaterialToolKit.xaml +++ b/Material.Styles/MaterialToolKit.xaml @@ -20,17 +20,24 @@ - - - - + + - + + - + + + + + + + + + @@ -39,11 +46,14 @@ + + + - + @@ -59,29 +69,41 @@ - - - - - + - - - + + - + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Material.Styles/MaterialToolKit.xaml.cs b/Material.Styles/MaterialToolKit.xaml.cs index ba7c4f5..2000659 100644 --- a/Material.Styles/MaterialToolKit.xaml.cs +++ b/Material.Styles/MaterialToolKit.xaml.cs @@ -3,11 +3,15 @@ using Avalonia.Animation; using Avalonia.Markup.Xaml; using Material.Styles.Additional; -namespace Material.Styles { - public class MaterialToolKit : Avalonia.Styling.Styles { - public MaterialToolKit() { +namespace Material.Styles +{ + public class MaterialToolKit : Avalonia.Styling.Styles + { + public MaterialToolKit() + { AvaloniaXamlLoader.Load(this); - Animation.RegisterAnimator(property => typeof(RelativePoint).IsAssignableFrom(property.PropertyType)); + Animation.RegisterAnimator(property => + typeof(RelativePoint).IsAssignableFrom(property.PropertyType)); } } } \ No newline at end of file diff --git a/Material.Styles/MaterialUnderline.xaml b/Material.Styles/MaterialUnderline.xaml index 3d847cc..99e796f 100644 --- a/Material.Styles/MaterialUnderline.xaml +++ b/Material.Styles/MaterialUnderline.xaml @@ -1,63 +1,64 @@ - - + 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/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 diff --git a/Material.Styles/MenuItem.xaml b/Material.Styles/MenuItem.xaml index 4868ab3..bd8e53c 100644 --- a/Material.Styles/MenuItem.xaml +++ b/Material.Styles/MenuItem.xaml @@ -2,8 +2,8 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 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 +159,7 @@ PlacementMode="Right" IsLightDismissEnabled="False" IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}"> - + @@ -170,7 +170,7 @@ Margin="0 8" /> - + @@ -229,7 +229,7 @@ IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}" IsLightDismissEnabled="False" WindowManagerAddShadowHint="False"> - + @@ -240,7 +240,7 @@ Margin="0,8" /> - + diff --git a/Material.Styles/Models/SnackbarModel.cs b/Material.Styles/Models/SnackbarModel.cs index 2d3826b..255f80f 100644 --- a/Material.Styles/Models/SnackbarModel.cs +++ b/Material.Styles/Models/SnackbarModel.cs @@ -11,13 +11,14 @@ namespace Material.Styles.Models _orientation = orientation; //_button = button; } - - public SnackbarModel(object content, TimeSpan duration, Orientation orientation = Orientation.Horizontal) : this(content, orientation) + + public SnackbarModel(object content, TimeSpan duration, Orientation orientation = Orientation.Horizontal) : + this(content, orientation) { _duration = duration; } - - + + private object _content; public object Content => _content; diff --git a/Material.Styles/NavigationDrawer.xaml b/Material.Styles/NavigationDrawer.xaml index 580a708..61b0ca7 100644 --- a/Material.Styles/NavigationDrawer.xaml +++ b/Material.Styles/NavigationDrawer.xaml @@ -1,82 +1,93 @@  + xmlns:assists="clr-namespace:Material.Styles.Assists" + xmlns:naming="clr-namespace:Material.Styles.Resources.Naming" + xmlns:controls="clr-namespace:Material.Styles.Controls"> - - - - - + + + + + - - - - - - - - - - + + + + + + + + - - - - - + + + + + + + - + - + + - - - - - - - - - + + + + + + - - + + + + - - - - - - + + +