diff --git a/.editorconfig b/.editorconfig
deleted file mode 100644
index b68becf..0000000
--- a/.editorconfig
+++ /dev/null
@@ -1,174 +0,0 @@
-root = true
-# editorconfig.org
-
-# top-most EditorConfig file
-
-# Default settings:
-# A newline ending every file
-# Use 4 spaces as indentation
-[*]
-insert_final_newline = false
-indent_style = space
-indent_size = 4
-
-# ReSharper properties
-resharper_place_simple_initializer_on_single_line = true
-resharper_wrap_before_arrow_with_expressions = false
-resharper_xml_wrap_lines = false
-dotnet_style_operator_placement_when_wrapping = beginning_of_line
-tab_width = 4
-end_of_line = crlf
-dotnet_style_coalesce_expression = true:suggestion
-dotnet_style_null_propagation = true:suggestion
-dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
-dotnet_style_prefer_auto_properties = true:silent
-dotnet_style_object_initializer = true:suggestion
-
-# C# files
-[*.cs]
-# New line preferences
-csharp_new_line_before_open_brace = none
-csharp_new_line_before_else = true
-csharp_new_line_before_catch = true
-csharp_new_line_before_finally = true
-csharp_new_line_before_members_in_object_initializers = false
-csharp_new_line_before_members_in_anonymous_types = true
-csharp_new_line_between_query_expression_clauses = true
-# trim_trailing_whitespace = true
-
-# Indentation preferences
-csharp_indent_block_contents = true
-csharp_indent_braces = false
-csharp_indent_case_contents = true
-csharp_indent_switch_labels = true
-csharp_indent_labels = one_less_than_current
-
-# avoid this. unless absolutely necessary
-dotnet_style_qualification_for_field = false:suggestion
-dotnet_style_qualification_for_property = false:suggestion
-dotnet_style_qualification_for_method = false:suggestion
-dotnet_style_qualification_for_event = false:suggestion
-
-# prefer var
-csharp_style_var_for_built_in_types = true
-csharp_style_var_when_type_is_apparent = true
-csharp_style_var_elsewhere = true:suggestion
-
-# use language keywords instead of BCL types
-dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
-dotnet_style_predefined_type_for_member_access = true:suggestion
-
-# name all constant fields using PascalCase
-dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
-dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
-dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
-
-dotnet_naming_symbols.constant_fields.applicable_kinds = field
-dotnet_naming_symbols.constant_fields.required_modifiers = const
-
-dotnet_naming_style.pascal_case_style.capitalization = pascal_case
-
-# internal and private fields should be _camelCase
-dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
-dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
-dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
-
-dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
-dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
-
-dotnet_naming_style.camel_case_underscore_style.required_prefix = _
-dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
-
-# use accessibility modifiers
-dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
-
-# Code style defaults
-dotnet_sort_system_directives_first = true
-csharp_preserve_single_line_blocks = true
-csharp_preserve_single_line_statements = false
-
-# Expression-level preferences
-dotnet_style_object_initializer = true:suggestion
-dotnet_style_collection_initializer = true:suggestion
-dotnet_style_explicit_tuple_names = true:suggestion
-dotnet_style_coalesce_expression = true:suggestion
-dotnet_style_null_propagation = true:suggestion
-
-# Expression-bodied members
-csharp_style_expression_bodied_methods = false:none
-csharp_style_expression_bodied_constructors = false:none
-csharp_style_expression_bodied_operators = false:none
-csharp_style_expression_bodied_properties = true:none
-csharp_style_expression_bodied_indexers = true:none
-csharp_style_expression_bodied_accessors = true:none
-
-# Pattern matching
-csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
-csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
-csharp_style_inlined_variable_declaration = true:suggestion
-
-# Null checking preferences
-csharp_style_throw_expression = true:suggestion
-csharp_style_conditional_delegate_call = true:suggestion
-
-# Space preferences
-csharp_space_after_cast = false
-csharp_space_after_colon_in_inheritance_clause = true
-csharp_space_after_comma = true
-csharp_space_after_dot = false
-csharp_space_after_keywords_in_control_flow_statements = true
-csharp_space_after_semicolon_in_for_statement = true
-csharp_space_around_binary_operators = before_and_after
-csharp_space_before_colon_in_inheritance_clause = true
-csharp_space_before_comma = false
-csharp_space_before_dot = false
-csharp_space_before_open_square_brackets = false
-csharp_space_before_semicolon_in_for_statement = false
-csharp_space_between_empty_square_brackets = false
-csharp_space_between_method_call_empty_parameter_list_parentheses = false
-csharp_space_between_method_call_name_and_opening_parenthesis = false
-csharp_space_between_method_call_parameter_list_parentheses = false
-csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
-csharp_space_between_method_declaration_name_and_open_parenthesis = false
-csharp_space_between_method_declaration_parameter_list_parentheses = false
-csharp_space_between_parentheses = false
-csharp_space_between_square_brackets = false
-space_within_single_line_array_initializer_braces = true
-csharp_using_directive_placement = outside_namespace:silent
-csharp_prefer_simple_using_statement = true:suggestion
-csharp_prefer_braces = when_multiline:silent
-csharp_style_namespace_declarations = file_scoped:silent
-csharp_style_prefer_method_group_conversion = true:silent
-csharp_style_prefer_top_level_statements = true:silent
-csharp_style_expression_bodied_lambdas = true:silent
-csharp_style_expression_bodied_local_functions = false:silent
-
-# Xaml files
-[*.{xaml,axaml}]
-indent_size = 2
-
-# Xml project files
-[*.{csproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}]
-indent_size = 2
-
-# Xml build files
-[*.builds]
-indent_size = 2
-
-# Xml files
-[*.{xml,stylecop,resx,ruleset}]
-indent_size = 2
-
-# Xml config files
-[*.{props,targets,config,nuspec}]
-indent_size = 2
-
-[*.json]
-indent_size = 2
-
-# Shell scripts
-[*.sh]
-end_of_line = lf
-
-[*.{cmd, bat}]
-end_of_line = crlf
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..dfe0770
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# Auto detect text files and perform LF normalization
+* text=auto
diff --git a/.github/workflows/prbuild.yml b/.github/workflows/prbuild.yml
deleted file mode 100644
index 59e291f..0000000
--- a/.github/workflows/prbuild.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-name: .NET Build on PR
-
-on:
- pull_request:
- branches: [ master ]
-
-jobs:
- build:
-
- runs-on: windows-latest
-
- steps:
- - uses: actions/checkout@v2
- - name: Setup .NET
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: 10
- - name: Setup Windows SDK 10.0.18362.0
- uses: GuillaumeFalourd/setup-windows10-sdk-action@v2
- with:
- sdk-version: 18362
- - name: Build and Pack
- run: |
- dotnet build Material.Icons -c Release /p:Platform="Any CPU"
- dotnet build Material.Icons.WPF -c Release /p:Platform="Any CPU"
- dotnet build Material.Icons.Avalonia -c Release /p:Platform="Any CPU"
- dotnet build Material.Icons.WinUI3 -c Release /p:Platform="Any CPU"
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
deleted file mode 100644
index 6168f36..0000000
--- a/.github/workflows/publish.yml
+++ /dev/null
@@ -1,33 +0,0 @@
-name: .NET Publish
-
-on:
- push:
- branches: [ master ]
-
-jobs:
- build:
-
- runs-on: windows-latest
-
- steps:
- - uses: actions/checkout@v2
- - name: Setup .NET
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: 10
- - name: Setup Windows SDK 10.0.18362.0
- uses: GuillaumeFalourd/setup-windows10-sdk-action@v2
- with:
- sdk-version: 18362
- - name: Build and Pack
- run: |
- dotnet build Material.Icons -c Release /p:Platform="Any CPU"
- dotnet build Material.Icons.WPF -c Release /p:Platform="Any CPU"
- dotnet build Material.Icons.Avalonia -c Release /p:Platform="Any CPU"
- dotnet build Material.Icons.WinUI3 -c Release /p:Platform="Any CPU"
- - name: Publish to Nuget
- run: |
- dotnet nuget push **/*.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 **/*.nupkg --api-key ${{secrets.GITHUB_TOKEN}} --source https://nuget.pkg.github.com/SKProCH/index.json --skip-duplicate
diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml
deleted file mode 100644
index 4df8687..0000000
--- a/.github/workflows/update.yml
+++ /dev/null
@@ -1,33 +0,0 @@
-name: Icons collection update
-
-on:
- schedule:
- - cron: '0 0,12 * * *'
- workflow_dispatch:
-
-jobs:
- build:
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v2
- with:
- persist-credentials: false
- - uses: actions/setup-dotnet@v4
- with:
- dotnet-version: 10
- - name: Cache .nuke/temp, ~/.nuget/packages
- uses: actions/cache@v4
- with:
- path: |
- .nuke/temp
- ~/.nuget/packages
- key: ${{ runner.os }}-${{ hashFiles('**/global.json', '**/*.csproj') }}
- - name: Run generator project
- run: ./build.sh GhUpdateIconsWorkflow
- - name: Push changes (if any)
- uses: actions-js/push@master
- with:
- github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
- message: 'Icons collection update ✨'
- branch: 'master'
diff --git a/.gitignore b/.gitignore
index 60ad021..6d902c2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,18 +1,7 @@
-bin/
-obj/
-/packages/
-riderModule.iml
-/_ReSharper.Caches/
-*.DotSettings
-
-# Created by https://www.toptal.com/developers/gitignore/api/csharp
-# Edit at https://www.toptal.com/developers/gitignore?templates=csharp
-
-### Csharp ###
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
-## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
@@ -34,6 +23,7 @@ mono_crash.*
[Rr]eleases/
x64/
x86/
+[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
@@ -72,6 +62,9 @@ project.lock.json
project.fragment.lock.json
artifacts/
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
# StyleCop
StyleCopReport.xml
@@ -81,7 +74,6 @@ StyleCopReport.xml
*_h.h
*.ilk
*.meta
-!*.Meta/
*.obj
*.iobj
*.pch
@@ -98,6 +90,7 @@ StyleCopReport.xml
*.tmp_proj
*_wpftmp.csproj
*.log
+*.tlog
*.vspscc
*.vssscc
.builds
@@ -150,7 +143,9 @@ _TeamCity*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
-coverage*[.json, .xml, .info]
+coverage*.json
+coverage*.xml
+coverage*.info
# Visual Studio code coverage results
*.coverage
@@ -189,7 +184,7 @@ publish/
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
-*.pubxml
+#*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
@@ -299,6 +294,17 @@ node_modules/
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
+# Visual Studio 6 auto-generated project file (contains which files were open etc.)
+*.vbp
+
+# Visual Studio 6 workspace and project file (working project files containing files to include in project)
+*.dsw
+*.dsp
+
+# Visual Studio 6 technical files
+*.ncb
+*.aps
+
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
@@ -355,6 +361,9 @@ ASALocalRun/
# Local History for Visual Studio
.localhistory/
+# Visual Studio History (VSHistory) files
+.vshistory/
+
# BeatPulse healthcheck temp database
healthchecksdb
@@ -364,6 +373,36 @@ MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
-# End of https://www.toptal.com/developers/gitignore/api/csharp
-.idea
-*.DotSettings
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
+
+# VS Code files for those working on multiple tools
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# Local History for Visual Studio Code
+.history/
+
+# Windows Installer files from build outputs
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# JetBrains Rider
+*.sln.iml
+/WateryTart.Platform.Linux/out
+/WateryTart.Platform.Linux/staging_folder
+WateryTart.Platform.Linux/WateryTart_Linux_amd64.deb
+WateryTart.Platform.Linux/WateryTart_Linux_arm64.deb
+*.deb
+*.parcel
+/.idea
+src/WateryTart.Platform.Linux/staging_folder/
+src/WateryTart.Platform.Linux/out/
+output/
diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json
index b6aae97..4a7b90c 100644
--- a/.nuke/build.schema.json
+++ b/.nuke/build.schema.json
@@ -24,9 +24,8 @@
"ExecutableTarget": {
"type": "string",
"enum": [
+ "Clean",
"Compile",
- "GhBumpVersionIfNeeded",
- "GhUpdateIconsWorkflow",
"Restore",
"UpdateIcons"
]
@@ -110,10 +109,6 @@
"Release"
]
},
- "SkipWpfCompilation": {
- "type": "boolean",
- "description": "Should we skip compilation of WPF projects. In non windows systems this is enabled by default"
- },
"Solution": {
"type": "string",
"description": "Path to a solution file that is automatically loaded"
diff --git a/.nuke/parameters.json b/.nuke/parameters.json
index 049f8bf..b7c89f2 100644
--- a/.nuke/parameters.json
+++ b/.nuke/parameters.json
@@ -1,4 +1,4 @@
{
- "$schema": "./build.schema.json",
- "Solution": "Material.Icons.sln"
-}
\ No newline at end of file
+ "$schema": "build.schema.json",
+ "Solution": "IconPackGenerators.slnx"
+}
diff --git a/AvaloniaApplication1/App.axaml b/AvaloniaApplication1/App.axaml
new file mode 100644
index 0000000..716b997
--- /dev/null
+++ b/AvaloniaApplication1/App.axaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AvaloniaApplication1/App.axaml.cs b/AvaloniaApplication1/App.axaml.cs
new file mode 100644
index 0000000..7149fe1
--- /dev/null
+++ b/AvaloniaApplication1/App.axaml.cs
@@ -0,0 +1,24 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+
+namespace AvaloniaApplication1
+{
+ public partial class App : Application
+ {
+ public override void Initialize()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override void OnFrameworkInitializationCompleted()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ desktop.MainWindow = new MainWindow();
+ }
+
+ base.OnFrameworkInitializationCompleted();
+ }
+ }
+}
\ No newline at end of file
diff --git a/AvaloniaApplication1/AvaloniaDemo.csproj b/AvaloniaApplication1/AvaloniaDemo.csproj
new file mode 100644
index 0000000..5daab8d
--- /dev/null
+++ b/AvaloniaApplication1/AvaloniaDemo.csproj
@@ -0,0 +1,29 @@
+
+
+ WinExe
+ net10.0
+ enable
+ app.manifest
+ true
+
+
+
+
+
+
+
+
+
+ None
+ All
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AvaloniaApplication1/MainWindow.axaml b/AvaloniaApplication1/MainWindow.axaml
new file mode 100644
index 0000000..a0a5ccc
--- /dev/null
+++ b/AvaloniaApplication1/MainWindow.axaml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AvaloniaApplication1/MainWindow.axaml.cs b/AvaloniaApplication1/MainWindow.axaml.cs
new file mode 100644
index 0000000..c018391
--- /dev/null
+++ b/AvaloniaApplication1/MainWindow.axaml.cs
@@ -0,0 +1,40 @@
+using Avalonia.Controls;
+using Avalonia.Media;
+using Avalonia.Threading;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace AvaloniaApplication1
+{
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+ Loaded += MainWindow_Loaded;
+ }
+
+ private void MainWindow_Loaded(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
+ {
+ LoadIconsAsync();
+ }
+
+ private async Task LoadIconsAsync()
+ {
+
+ //var allIcons = Enum.GetValues(typeof(FontAwesome.Icons.FontAwesomeIconKind)).Cast();
+ //foreach (var icon in allIcons)
+ //{
+ // // Ensure UI update happens on the UI thread
+ // await Dispatcher.UIThread.InvokeAsync(() =>
+ // {
+ // faIcon.Kind = icon;
+ // });
+
+ // // Wait for 1 second
+ // await Task.Delay(200);
+ //}
+ }
+ }
+}
\ No newline at end of file
diff --git a/Material.Icons.Avalonia.Demo/Program.cs b/AvaloniaApplication1/Program.cs
similarity index 64%
rename from Material.Icons.Avalonia.Demo/Program.cs
rename to AvaloniaApplication1/Program.cs
index 9532944..e2d6008 100644
--- a/Material.Icons.Avalonia.Demo/Program.cs
+++ b/AvaloniaApplication1/Program.cs
@@ -1,19 +1,22 @@
using Avalonia;
-using Avalonia.ReactiveUI;
+using System;
-namespace Material.Icons.Avalonia.Demo {
- class Program {
+namespace AvaloniaApplication1
+{
+ internal class Program
+ {
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
+ [STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
- .StartWithClassicDesktopLifetime(args);
+ .StartWithClassicDesktopLifetime(args);
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure()
- .UsePlatformDetect()
- .LogToTrace()
- .UseReactiveUI();
+ .UsePlatformDetect()
+ .WithInterFont()
+ .LogToTrace();
}
-}
\ No newline at end of file
+}
diff --git a/AvaloniaApplication1/app.manifest b/AvaloniaApplication1/app.manifest
new file mode 100644
index 0000000..3943700
--- /dev/null
+++ b/AvaloniaApplication1/app.manifest
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/IconGeneratorTemplate.Avalonia/DummyIcon.axaml b/IconGeneratorTemplate.Avalonia/DummyIcon.axaml
new file mode 100644
index 0000000..381b192
--- /dev/null
+++ b/IconGeneratorTemplate.Avalonia/DummyIcon.axaml
@@ -0,0 +1,139 @@
+
+ 0:0:2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/IconGeneratorTemplate.Avalonia/DummyIcon.axaml.cs b/IconGeneratorTemplate.Avalonia/DummyIcon.axaml.cs
new file mode 100644
index 0000000..9cef877
--- /dev/null
+++ b/IconGeneratorTemplate.Avalonia/DummyIcon.axaml.cs
@@ -0,0 +1,137 @@
+using Avalonia;
+using Avalonia.Controls.Primitives;
+using Avalonia.Interactivity;
+using Avalonia.Media;
+using IconGenerators;
+
+namespace Dummy.Icons.Avalonia;
+
+public class DummyIcon : TemplatedControl, IImage {
+ #region Properties
+
+ public static readonly StyledProperty KindProperty
+ = AvaloniaProperty.Register(nameof(Kind));
+
+ ///
+ /// Gets or sets the icon to display.
+ ///
+ public DummyIconKind? Kind {
+ get => GetValue(KindProperty);
+ set => SetValue(KindProperty, value);
+ }
+
+ public static readonly StyledProperty IconSizeProperty =
+ AvaloniaProperty.Register(nameof(IconSize), defaultValue: double.NaN);
+
+ ///
+ /// Gets or sets the uniform size of the icon.
+ ///
+ public double IconSize {
+ get => GetValue(IconSizeProperty);
+ set => SetValue(IconSizeProperty, value);
+ }
+
+ public static readonly StyledProperty AnimationProperty
+ = AvaloniaProperty.Register(nameof(Animation));
+
+ ///
+ /// Gets or sets the icon animation to play.
+ ///
+ public DummyIconAnimation Animation {
+ get => GetValue(AnimationProperty);
+ set => SetValue(AnimationProperty, value);
+ }
+
+ public static readonly DirectProperty DrawingProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(Drawing),
+ o => o.Drawing);
+
+ ///
+ /// Gets the of the icon.
+ ///
+ public GeometryDrawing Drawing { get; } = new();
+
+ // Default size for FontAwesome Icons
+ private static readonly Rect DefaultIconBounds = new(0, 0, 24, 24);
+
+ #endregion
+
+ #region Constructor
+
+ static DummyIcon() {
+ DummyIconsUtils.InitializeGeometryParser();
+ }
+
+ public DummyIcon() {
+ Drawing.Brush = Foreground;
+ }
+
+ #endregion
+
+ #region Overrides
+
+ ///
+ protected override void OnLoaded(RoutedEventArgs e) {
+ if (Drawing.Geometry is null)
+ SetGeometry();
+ base.OnLoaded(e);
+ }
+
+ ///
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e) {
+ base.OnPropertyChanged(e);
+
+ if (e.Property == KindProperty) {
+ SetGeometry();
+ }
+ else if (e.Property == ForegroundProperty) {
+ Drawing.Brush = Foreground;
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Sets the geometry for the drawing based on the specified FontAwesome icon kind.
+ ///
+ /// This method updates the Geometry property by parsing the
+ /// geometry data associated with the current value.
+ ///
+ private void SetGeometry() {
+ Drawing.Geometry = Kind is null ? null : DummyIconDataProvider.Get(Kind.Value);
+ }
+
+ #endregion
+
+ #region IImage Implementation
+
+ ///
+ Size IImage.Size => DefaultIconBounds.Size;
+
+ ///
+ void IImage.Draw(DrawingContext context, Rect sourceRect, Rect destRect) {
+ if (Drawing.Geometry is null)
+ SetGeometry();
+
+ var bounds = DefaultIconBounds;
+ var scale = Matrix.CreateScale(
+ destRect.Width / sourceRect.Width,
+ destRect.Height / sourceRect.Height
+ );
+ var translate = Matrix.CreateTranslation(
+ -sourceRect.X + destRect.X - bounds.X,
+ -sourceRect.Y + destRect.Y - bounds.Y
+ );
+
+ using (context.PushClip(destRect))
+ using (context.PushTransform(translate * scale)) {
+ Drawing.Draw(context);
+ }
+ }
+
+ #endregion
+
+}
diff --git a/IconGeneratorTemplate.Avalonia/DummyIconAnimation.cs b/IconGeneratorTemplate.Avalonia/DummyIconAnimation.cs
new file mode 100644
index 0000000..2d6a4b0
--- /dev/null
+++ b/IconGeneratorTemplate.Avalonia/DummyIconAnimation.cs
@@ -0,0 +1,12 @@
+namespace Dummy.Icons.Avalonia;
+
+public enum DummyIconAnimation
+{
+ None,
+ Spin,
+ SpinCcw,
+ Pulse,
+ PulseCcw,
+ FadeOutIn,
+ FadeInOut
+}
diff --git a/IconGeneratorTemplate.Avalonia/DummyIconDataProvider.cs b/IconGeneratorTemplate.Avalonia/DummyIconDataProvider.cs
new file mode 100644
index 0000000..893cb15
--- /dev/null
+++ b/IconGeneratorTemplate.Avalonia/DummyIconDataProvider.cs
@@ -0,0 +1,113 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using IconGenerators.Avalonia;
+
+namespace IconGenerators;
+
+
+///
+/// Provides access to the Material Design icons data and its geometry.
+///
+public partial class DummyIconDataProvider : IIconProvider
+{
+ private static DummyIconDataProvider _instance = new();
+ private static Func? _parser;
+
+ ///
+ /// Gets the cache singleton for the icons. The cache is used to store the parsed icons to avoid parsing them multiple times.
+ ///
+ private static Dictionary? _cache = new();
+
+ ///
+ /// Gets the cache for the icons. The cache is used to store the parsed icons to avoid parsing them multiple times.
+ ///
+ public static IReadOnlyDictionary? Cache => _cache;
+
+ ///
+ /// Gets or sets the singleton instance of this provider
+ ///
+ public static DummyIconDataProvider Instance
+ {
+ get => _instance;
+ set
+ {
+ _instance = value ?? throw new ArgumentNullException(nameof(value));
+ ClearCache();
+ }
+ }
+
+ ///
+ /// Disables the cache for the icons.
+ ///
+ public static void DisableCache()
+ {
+ _cache = null;
+ }
+
+ ///
+ /// Clears the cache for the icons.
+ ///
+ public static void ClearCache()
+ {
+ _cache?.Clear();
+ }
+
+ ///
+ /// Initializes the geometry parser with the specified parsing function.
+ ///
+ /// This method sets the parser function to be used for geometry parsing operations. If the
+ /// parser has already been initialized, subsequent calls to this method will have no effect.
+ /// A function that takes a string input and returns an object representing the parsed geometry. This parameter
+ /// cannot be null.
+ public static void InitializeGeometryParser(Func parser) => _parser ??= parser;
+
+ ///
+ /// Gets the geometry for the specified icon using the
+ ///
+ /// The icon kind
+ /// SVG path for target icon kind
+ public static T Get(DummyIconKind kind) where T : class
+ {
+ if (_cache?.TryGetValue(kind, out var value) is true)
+ {
+ return value as T ?? throw new InvalidOperationException(
+ "Invalid type for icon kind. Check that you are requesting the correct geometry type.");
+ }
+
+ if (_parser is null)
+ {
+ throw new InvalidOperationException(
+ "Geometry parser not initialized. Call InitializeGeometryParser first.");
+ }
+
+ var result = _parser(GetData(kind)) as T ?? throw new InvalidOperationException(
+ "Parser returns a wrong type. Check that you are requesting the correct geometry type.");
+
+ if (_cache != null)
+ _cache[kind] = result;
+
+ return result;
+ }
+
+ ///
+ /// Gets the data for the specified icon using the
+ ///
+ /// The icon kind
+ /// SVG path for target icon kind
+ public static string GetData(DummyIconKind kind) => Instance.ProvideData(kind);
+
+ ///
+ /// Provides the data for the specified icon kind
+ ///
+ /// The icon kind
+ /// SVG path for target icon kind
+ public virtual partial string ProvideData(DummyIconKind kind);
+
+ public string ProvideData(string kindstring)
+ {
+ // Using Enum.Parse (throws if invalid)
+ DummyIconKind kind = (DummyIconKind)Enum.Parse(typeof(DummyIconKind), kindstring);
+ return ProvideData(kind);
+ }
+}
diff --git a/IconGeneratorTemplate.Avalonia/DummyIconExt.cs b/IconGeneratorTemplate.Avalonia/DummyIconExt.cs
new file mode 100644
index 0000000..e423dd5
--- /dev/null
+++ b/IconGeneratorTemplate.Avalonia/DummyIconExt.cs
@@ -0,0 +1,125 @@
+using System;
+using Avalonia.Controls.Primitives;
+using Avalonia.Data;
+using Avalonia.Layout;
+using Avalonia.Markup.Xaml;
+using Avalonia.Media;
+using IconGenerators;
+
+namespace Dummy.Icons.Avalonia;
+public class DummyIconExt : MarkupExtension {
+ public DummyIconExt() { }
+ public DummyIconExt(DummyIconKind kind) {
+ Kind = kind;
+ }
+
+ public DummyIconExt(DummyIconKind kind, DummyIconAnimation animation) {
+ Kind = kind;
+ Animation = animation;
+ }
+
+ public DummyIconExt(DummyIconKind kind, double iconSize, DummyIconAnimation animation = DummyIconAnimation.None) {
+ Kind = kind;
+ IconSize = iconSize;
+ Animation = animation;
+ }
+
+ ///
+ /// Gets or sets the icon to display. Provides IntelliSense autocomplete.
+ ///
+ [ConstructorArgument("kind")]
+ public DummyIconKind Kind { get; set; }
+
+ ///
+ /// Gets or sets a binding for the icon kind. Use this when data binding is required.
+ ///
+ [ConstructorArgument("kindBinding")]
+ public IBinding? KindBinding { get; set; }
+
+ ///
+ /// Gets or sets the size of the icon to display.
+ /// Can be a double or a binding.
+ ///
+ [ConstructorArgument("iconSize")]
+ public object? IconSize { get; set; }
+
+ ///
+ /// Gets or sets the icon foreground brush.
+ ///
+ [ConstructorArgument("iconForeground")]
+ public IBrush? IconForeground { get; set; }
+
+ ///
+ /// Gets or sets a binding for the icon foreground. Use this when data binding is required.
+ ///
+ [ConstructorArgument("iconForegroundBinding")]
+ public IBinding? IconForegroundBinding { get; set; }
+
+ ///
+ /// Gets or sets the animation to play. Provides IntelliSense autocomplete.
+ ///
+ [ConstructorArgument("animation")]
+ public DummyIconAnimation Animation { get; set; }
+
+ ///
+ /// Gets or sets a binding for the animation. Use this when data binding is required.
+ ///
+ [ConstructorArgument("animationBinding")]
+ public IBinding? AnimationBinding { get; set; }
+
+ ///
+ /// Gets or sets the vertical alignment of the content.
+ ///
+ [ConstructorArgument("verticalAlignment")]
+ public VerticalAlignment? VerticalAlignment { get; set; }
+
+ ///
+ /// Gets or sets the horizontal alignment of the content.
+ ///
+ [ConstructorArgument("horizontalAlignment")]
+ public HorizontalAlignment? HorizontalAlignment { get; set; }
+
+ ///
+ /// Gets or sets the class names to apply to the element.
+ ///
+ [ConstructorArgument("classes")]
+ public string? Classes { get; set; }
+
+ public override object ProvideValue(IServiceProvider serviceProvider) {
+ var result = new DummyIcon();
+
+ // Kind: binding takes precedence
+ if (KindBinding is not null) result.Bind(DummyIcon.KindProperty, KindBinding);
+ else result.Kind = Kind;
+
+ // Animation: binding takes precedence
+ if (AnimationBinding is not null) result.Bind(DummyIcon.AnimationProperty, AnimationBinding);
+ else result.Animation = Animation;
+
+ if (IconSize is not null) {
+ switch (IconSize) {
+ case IBinding binding:
+ result.Bind(DummyIcon.IconSizeProperty, binding);
+ break;
+ case IConvertible conv:
+ result.IconSize = conv.ToDouble(System.Globalization.CultureInfo.InvariantCulture);
+ break;
+ default:
+ throw new InvalidOperationException($"IconSize must be of type IBinding or IConvertible. Actual type: {IconSize.GetType().FullName}");
+ }
+ }
+
+ // IconForeground: binding takes precedence
+ if (IconForegroundBinding is not null) result.Bind(TemplatedControl.ForegroundProperty, IconForegroundBinding);
+ else if (IconForeground is not null) result.Foreground = IconForeground;
+
+ if (VerticalAlignment is not null) result.VerticalAlignment = VerticalAlignment.Value;
+ if (HorizontalAlignment is not null) result.HorizontalAlignment = HorizontalAlignment.Value;
+
+ if (!string.IsNullOrWhiteSpace(Classes)) {
+ result.Classes.AddRange(global::Avalonia.Controls.Classes.Parse(Classes!));
+ }
+
+ return result;
+ }
+}
diff --git a/IconGeneratorTemplate.Avalonia/DummyIconStyles.axaml b/IconGeneratorTemplate.Avalonia/DummyIconStyles.axaml
new file mode 100644
index 0000000..2da223b
--- /dev/null
+++ b/IconGeneratorTemplate.Avalonia/DummyIconStyles.axaml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/IconGeneratorTemplate.Avalonia/DummyIconText.axaml b/IconGeneratorTemplate.Avalonia/DummyIconText.axaml
new file mode 100644
index 0000000..bb0706b
--- /dev/null
+++ b/IconGeneratorTemplate.Avalonia/DummyIconText.axaml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/IconGeneratorTemplate.Avalonia/DummyIconText.axaml.cs b/IconGeneratorTemplate.Avalonia/DummyIconText.axaml.cs
new file mode 100644
index 0000000..dc238c6
--- /dev/null
+++ b/IconGeneratorTemplate.Avalonia/DummyIconText.axaml.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Metadata;
+using Avalonia.Controls.Primitives;
+using Avalonia.Layout;
+
+namespace Dummy.Icons.Avalonia;
+
+[TemplatePart("PART_Icon", typeof(DummyIcon))]
+[TemplatePart("PART_TextBlock", typeof(TextBlock))]
+[TemplatePart("PART_SelectableTextBlock", typeof(SelectableTextBlock))]
+public class DummyIconText : DummyIcon {
+ public static readonly StyledProperty IconPlacementProperty =
+ DockPanel.DockProperty.AddOwner();
+
+ public static readonly StyledProperty SpacingProperty =
+ StackPanel.SpacingProperty.AddOwner();
+
+ public static readonly StyledProperty OrientationProperty =
+ StackPanel.OrientationProperty.AddOwner();
+
+ public static readonly StyledProperty TextProperty =
+ TextBlock.TextProperty.AddOwner();
+
+ public static readonly StyledProperty IsTextSelectableProperty =
+ AvaloniaProperty.Register(nameof(IsTextSelectable));
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty HorizontalContentAlignmentProperty =
+ ContentControl.HorizontalContentAlignmentProperty.AddOwner();
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty VerticalContentAlignmentProperty =
+ ContentControl.VerticalContentAlignmentProperty.AddOwner();
+
+ ///
+ /// Gets or sets the icon placement relative to the text.
+ ///
+ public Dock IconPlacement {
+ get => GetValue(IconPlacementProperty);
+ set => SetValue(IconPlacementProperty, value);
+ }
+
+ ///
+ /// Gets or sets the spacing between the icon and the text.
+ ///
+ public double Spacing {
+ get => GetValue(SpacingProperty);
+ set => SetValue(SpacingProperty, value);
+ }
+
+ ///
+ /// Gets or sets the orientation in which the icon and the text will be layed out.
+ ///
+ public Orientation Orientation {
+ get => GetValue(OrientationProperty);
+ set => SetValue(OrientationProperty, value);
+ }
+
+ ///
+ /// Gets or sets the text to display
+ ///
+ public string? Text {
+ get => GetValue(TextProperty);
+ set => SetValue(TextProperty, value);
+ }
+
+ ///
+ /// Gets or sets whether the text should be selectable
+ ///
+ public bool IsTextSelectable {
+ get => GetValue(IsTextSelectableProperty);
+ set => SetValue(IsTextSelectableProperty, value);
+ }
+
+ ///
+ /// Gets or sets the horizontal alignment of the content within the control.
+ ///
+ public HorizontalAlignment HorizontalContentAlignment {
+ get => GetValue(HorizontalContentAlignmentProperty);
+ set => SetValue(HorizontalContentAlignmentProperty, value);
+ }
+
+ ///
+ /// Gets or sets the vertical alignment of the content within the control.
+ ///
+ public VerticalAlignment VerticalContentAlignment {
+ get => GetValue(VerticalContentAlignmentProperty);
+ set => SetValue(VerticalContentAlignmentProperty, value);
+ }
+
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e) {
+ base.OnApplyTemplate(e);
+
+
+ if (Classes.Count > 0) {
+ // Ignore pseudo-classes replication, otherwise crash with: The pseudoclass ':xxx' may only be added by the control itself.
+ var filteredClasses = Classes.Where(c => c.Length > 0 && c[0] != ':').ToArray();
+
+ if (filteredClasses.Length > 0) {
+ // Redirect classes to the template parts
+ var icon = e.NameScope.Get("PART_Icon");
+ var textBlock = e.NameScope.Get("PART_TextBlock");
+ var selectableTextBlock = e.NameScope.Get("PART_SelectableTextBlock");
+
+ icon.Classes.AddRange(filteredClasses);
+ textBlock.Classes.AddRange(filteredClasses);
+ selectableTextBlock.Classes.AddRange(filteredClasses);
+ }
+ }
+ }
+}
diff --git a/IconGeneratorTemplate.Avalonia/DummyIconTextExt.cs b/IconGeneratorTemplate.Avalonia/DummyIconTextExt.cs
new file mode 100644
index 0000000..c95afac
--- /dev/null
+++ b/IconGeneratorTemplate.Avalonia/DummyIconTextExt.cs
@@ -0,0 +1,114 @@
+using System;
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Data;
+using Avalonia.Layout;
+using Avalonia.Markup.Xaml;
+using IconGenerators;
+
+namespace Dummy.Icons.Avalonia;
+
+public class DummyIconTextExt : DummyIconExt {
+ public DummyIconTextExt() { }
+
+ public DummyIconTextExt(DummyIconKind kind) : base(kind)
+ {
+ }
+
+ public DummyIconTextExt(DummyIconKind kind, DummyIconAnimation animation) : base(kind, animation)
+ {
+ }
+
+ public DummyIconTextExt(DummyIconKind kind, double iconSize, DummyIconAnimation animation = DummyIconAnimation.None) : base(kind, iconSize, animation)
+ {
+ }
+
+ public DummyIconTextExt(DummyIconKind kind, string text, DummyIconAnimation animation = DummyIconAnimation.None)
+ : base(kind, animation)
+ {
+ Text = text;
+ }
+
+ public DummyIconTextExt(DummyIconKind kind, double iconSize, string text, DummyIconAnimation animation = DummyIconAnimation.None)
+ : base(kind, iconSize, animation)
+ {
+ Text = text;
+ }
+
+ public DummyIconTextExt(DummyIconKind kind, double iconSize, Dock iconPlacement, string text, DummyIconAnimation animation = DummyIconAnimation.None)
+ : base(kind, iconSize, animation) {
+ IconPlacement = iconPlacement;
+ Text = text;
+ }
+
+ [ConstructorArgument("iconPlacement")]
+ public Dock? IconPlacement { get; set; }
+
+ [ConstructorArgument("spacing")]
+ public double? Spacing { get; set; }
+
+ [ConstructorArgument("text")]
+ public object? Text { get; set; }
+
+ [ConstructorArgument("isTextSelectable")]
+ public bool? IsTextSelectable { get; set; }
+
+ [ConstructorArgument("verticalContentAlignment")]
+ public VerticalAlignment? VerticalContentAlignment { get; set; }
+
+ [ConstructorArgument("horizontalContentAlignment")]
+ public HorizontalAlignment? HorizontalContentAlignment { get; set; }
+
+ public override object ProvideValue(IServiceProvider serviceProvider) {
+ // If no text is provided and it's not a binding, fall back to base
+ if (Text is null || (Text is not IBinding && Text is string textString && string.IsNullOrWhiteSpace(textString)))
+ return base.ProvideValue(serviceProvider);
+
+ var result = new DummyIconText();
+
+ // Kind: binding takes precedence
+ if (KindBinding is not null) result.Bind(DummyIcon.KindProperty, KindBinding);
+ else result.Kind = Kind;
+
+ // Animation: binding takes precedence
+ if (AnimationBinding is not null) result.Bind(DummyIcon.AnimationProperty, AnimationBinding);
+ else result.Animation = Animation;
+
+ // Apply Text (supports binding or direct value)
+ if (Text is IBinding textBinding) result.Bind(DummyIconText.TextProperty, textBinding);
+ else if (Text is string textValue) result.Text = textValue;
+
+ if (IconSize is not null) {
+ switch (IconSize) {
+ case IBinding binding:
+ result.Bind(DummyIcon.IconSizeProperty, binding);
+ break;
+ case IConvertible conv:
+ result.IconSize = conv.ToDouble(System.Globalization.CultureInfo.InvariantCulture);
+ break;
+ default:
+ throw new InvalidOperationException($"IconSize must be of type IBinding or IConvertible. Actual type: {IconSize.GetType().FullName}");
+ }
+ }
+
+ // IconForeground: binding takes precedence
+ if (IconForegroundBinding is not null) result.Bind(TemplatedControl.ForegroundProperty, IconForegroundBinding);
+ else if (IconForeground is not null) result.Foreground = IconForeground;
+
+ if (IconPlacement is not null) result.IconPlacement = IconPlacement.Value;
+
+ if (Spacing is not null) result.Spacing = Spacing.Value;
+ if (IsTextSelectable is not null) result.IsTextSelectable = IsTextSelectable.Value;
+
+ if (VerticalAlignment is not null) result.VerticalAlignment = VerticalAlignment.Value;
+ if (HorizontalAlignment is not null) result.HorizontalAlignment = HorizontalAlignment.Value;
+ if (VerticalContentAlignment is not null) result.VerticalContentAlignment = VerticalContentAlignment.Value;
+ if (HorizontalContentAlignment is not null) result.HorizontalContentAlignment = HorizontalContentAlignment.Value;
+
+ if (!string.IsNullOrWhiteSpace(Classes)) {
+ result.Classes.AddRange(global::Avalonia.Controls.Classes.Parse(Classes!));
+ }
+
+ return result;
+ }
+}
diff --git a/IconGeneratorTemplate.Avalonia/DummyIconsUtils.cs b/IconGeneratorTemplate.Avalonia/DummyIconsUtils.cs
new file mode 100644
index 0000000..e0bc0d5
--- /dev/null
+++ b/IconGeneratorTemplate.Avalonia/DummyIconsUtils.cs
@@ -0,0 +1,7 @@
+using Avalonia.Media;
+using IconGenerators;
+
+namespace Dummy.Icons.Avalonia;
+internal static class DummyIconsUtils {
+ public static void InitializeGeometryParser() => DummyIconDataProvider.InitializeGeometryParser(Geometry.Parse);
+}
\ No newline at end of file
diff --git a/IconGeneratorTemplate.Avalonia/DummyIconstyles.axaml.cs b/IconGeneratorTemplate.Avalonia/DummyIconstyles.axaml.cs
new file mode 100644
index 0000000..cb03681
--- /dev/null
+++ b/IconGeneratorTemplate.Avalonia/DummyIconstyles.axaml.cs
@@ -0,0 +1,18 @@
+using System;
+using Avalonia.Markup.Xaml;
+using Avalonia.Styling;
+
+namespace Dummy.Icons.Avalonia;
+///
+/// Styles for Dummy.Icons.Avalonia library
+///
+public sealed class DummyIconStyles : Styles {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The parent's service provider.
+ public DummyIconStyles(IServiceProvider? serviceProvider) {
+ DummyIconsUtils.InitializeGeometryParser();
+ AvaloniaXamlLoader.Load(serviceProvider, this);
+ }
+}
diff --git a/IconGeneratorTemplate.Avalonia/IconGeneratorTemplate.Avalonia.csproj b/IconGeneratorTemplate.Avalonia/IconGeneratorTemplate.Avalonia.csproj
new file mode 100644
index 0000000..3007759
--- /dev/null
+++ b/IconGeneratorTemplate.Avalonia/IconGeneratorTemplate.Avalonia.csproj
@@ -0,0 +1,71 @@
+
+
+ Library
+ net10.0
+ enable
+ true
+
+
+
+
+ true
+ 1.0.0
+ your_name
+ your_company
+ https://github.com/TemuWolverine/Material.Icons
+ https://github.com/TemuWolverine/Material.Icons
+ git
+ readme.md
+ True
+ snupkg
+ true
+ true
+ true
+ snupkg
+ .pdb
+
+
+
+
+
+
+ Always
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ None
+ All
+
+
+
+
+
+
+
+
+
+
+ True
+ \
+
+
+
+
+
+
+
+
+
+
+
diff --git a/IconGeneratorTemplate.Avalonia/PulseEasing.cs b/IconGeneratorTemplate.Avalonia/PulseEasing.cs
new file mode 100644
index 0000000..88edd3f
--- /dev/null
+++ b/IconGeneratorTemplate.Avalonia/PulseEasing.cs
@@ -0,0 +1,17 @@
+using Avalonia.Animation.Easings;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Dummy.Icons.Avalonia;
+public class PulseEasing : Easing {
+ private const int Steps = 8;
+
+ private static readonly IEnumerable _steps = Enumerable
+ .Range(0, Steps + 1)
+ .Select(index => 1.0 / Steps * index)
+ .ToArray();
+
+ public override double Ease(double progress) {
+ return _steps.Last(step => step <= progress);
+ }
+}
diff --git a/IconGeneratorTemplate.Avalonia/readme.md b/IconGeneratorTemplate.Avalonia/readme.md
new file mode 100644
index 0000000..82ae7be
--- /dev/null
+++ b/IconGeneratorTemplate.Avalonia/readme.md
@@ -0,0 +1 @@
+test readme
\ No newline at end of file
diff --git a/Material.Icons/MaterialIconAnimationEnum.cs b/IconGenerators.Avalonia/DummyIconAnimation.cs
similarity index 57%
rename from Material.Icons/MaterialIconAnimationEnum.cs
rename to IconGenerators.Avalonia/DummyIconAnimation.cs
index b9a880d..b8bce9b 100644
--- a/Material.Icons/MaterialIconAnimationEnum.cs
+++ b/IconGenerators.Avalonia/DummyIconAnimation.cs
@@ -1,6 +1,7 @@
-namespace Material.Icons;
+namespace IconGenerators.Avalonia;
-public enum MaterialIconAnimation {
+public enum IconAnimation
+{
None,
Spin,
SpinCcw,
diff --git a/IconGenerators.Avalonia/DummyIconsUtils.cs b/IconGenerators.Avalonia/DummyIconsUtils.cs
new file mode 100644
index 0000000..a9e54a3
--- /dev/null
+++ b/IconGenerators.Avalonia/DummyIconsUtils.cs
@@ -0,0 +1,7 @@
+using Avalonia.Media;
+using IconGenerators;
+
+namespace Dummy.Icons.Avalonia;
+internal static class DummyIconsUtils {
+// public static void InitializeGeometryParser() => DummyIconDataProvider.InitializeGeometryParser(Geometry.Parse);
+}
\ No newline at end of file
diff --git a/IconGenerators.Avalonia/IIconProvider.cs b/IconGenerators.Avalonia/IIconProvider.cs
new file mode 100644
index 0000000..05744c1
--- /dev/null
+++ b/IconGenerators.Avalonia/IIconProvider.cs
@@ -0,0 +1,8 @@
+using Avalonia.Media;
+
+namespace IconGenerators.Avalonia;
+
+public interface IIconProvider
+{
+ string ProvideData(string kind);
+}
diff --git a/IconGenerators.Avalonia/IconGenerators.Avalonia.csproj b/IconGenerators.Avalonia/IconGenerators.Avalonia.csproj
new file mode 100644
index 0000000..0f293d9
--- /dev/null
+++ b/IconGenerators.Avalonia/IconGenerators.Avalonia.csproj
@@ -0,0 +1,26 @@
+
+
+ Library
+ net10.0
+ enable
+ true
+
+
+
+
+
+
+
+
+
+ None
+ All
+
+
+
+
+
+ UniversalIconStyles.axaml
+
+
+
diff --git a/IconGenerators.Avalonia/PulseEasing.cs b/IconGenerators.Avalonia/PulseEasing.cs
new file mode 100644
index 0000000..362f7e4
--- /dev/null
+++ b/IconGenerators.Avalonia/PulseEasing.cs
@@ -0,0 +1,18 @@
+using Avalonia.Animation.Easings;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace IconGenerators.Avalonia;
+
+public class PulseEasing : Easing {
+ private const int Steps = 8;
+
+ private static readonly IEnumerable _steps = Enumerable
+ .Range(0, Steps + 1)
+ .Select(index => 1.0 / Steps * index)
+ .ToArray();
+
+ public override double Ease(double progress) {
+ return _steps.Last(step => step <= progress);
+ }
+}
diff --git a/IconGenerators.Avalonia/UniverasalIcon.axaml b/IconGenerators.Avalonia/UniverasalIcon.axaml
new file mode 100644
index 0000000..1ec27a1
--- /dev/null
+++ b/IconGenerators.Avalonia/UniverasalIcon.axaml
@@ -0,0 +1,136 @@
+
+ 0:0:2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/IconGenerators.Avalonia/UniversalIcon.axaml.cs b/IconGenerators.Avalonia/UniversalIcon.axaml.cs
new file mode 100644
index 0000000..2d7b20e
--- /dev/null
+++ b/IconGenerators.Avalonia/UniversalIcon.axaml.cs
@@ -0,0 +1,140 @@
+using Avalonia;
+using Avalonia.Controls.Primitives;
+using Avalonia.Interactivity;
+using Avalonia.Media;
+
+namespace IconGenerators.Avalonia;
+
+public class UniversalIcon : TemplatedControl, IImage
+{
+ #region Properties
+
+ public static readonly StyledProperty ProviderProperty =
+ AvaloniaProperty.Register(nameof(Provider));
+
+ public IIconProvider? Provider
+ {
+ get => GetValue(ProviderProperty);
+ set => SetValue(ProviderProperty, value);
+ }
+
+ public static readonly StyledProperty IconProperty =
+ AvaloniaProperty.Register(nameof(Icon));
+
+ public string? Icon
+ {
+ get => GetValue(IconProperty);
+ set => SetValue(IconProperty, value);
+ }
+
+ public static readonly StyledProperty IconSizeProperty =
+ AvaloniaProperty.Register(nameof(IconSize), defaultValue: double.NaN);
+
+ public double IconSize
+ {
+ get => GetValue(IconSizeProperty);
+ set => SetValue(IconSizeProperty, value);
+ }
+
+ public static readonly StyledProperty AnimationProperty
+ = AvaloniaProperty.Register(nameof(Animation));
+
+ ///
+ /// Gets or sets the icon animation to play.
+ ///
+ public IconAnimation Animation
+ {
+ get => GetValue(AnimationProperty);
+ set => SetValue(AnimationProperty, value);
+ }
+
+ public static readonly DirectProperty DrawingProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(Drawing),
+ o => o.Drawing);
+
+ public GeometryDrawing Drawing { get; } = new();
+
+ private static readonly Rect DefaultIconBounds = new(0, 0, 24, 24);
+
+ #endregion
+
+ #region Constructor
+
+ public UniversalIcon()
+ {
+ Drawing.Brush = Foreground;
+ }
+
+ #endregion
+
+ #region Overrides
+
+ protected override void OnLoaded(RoutedEventArgs e)
+ {
+ if (Drawing.Geometry is null)
+ SetGeometry();
+ base.OnLoaded(e);
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ base.OnPropertyChanged(e);
+
+ if (e.Property == ProviderProperty || e.Property == IconProperty)
+ {
+ SetGeometry();
+ }
+ else if (e.Property == ForegroundProperty)
+ {
+ Drawing.Brush = Foreground;
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ private void SetGeometry()
+ {
+ if (Provider is not null && !string.IsNullOrEmpty(Icon))
+ {
+ var data = Provider.ProvideData(Icon);
+ Drawing.Geometry = !string.IsNullOrEmpty(data) ? Geometry.Parse(data) : null;
+ }
+ else
+ {
+ Drawing.Geometry = null;
+ }
+ }
+
+ #endregion
+
+ #region IImage Implementation
+
+ Size IImage.Size => DefaultIconBounds.Size;
+
+ void IImage.Draw(DrawingContext context, Rect sourceRect, Rect destRect)
+ {
+ if (Drawing.Geometry is null)
+ SetGeometry();
+
+ var bounds = DefaultIconBounds;
+ var scale = Matrix.CreateScale(
+ destRect.Width / sourceRect.Width,
+ destRect.Height / sourceRect.Height
+ );
+ var translate = Matrix.CreateTranslation(
+ -sourceRect.X + destRect.X - bounds.X,
+ -sourceRect.Y + destRect.Y - bounds.Y
+ );
+
+ using (context.PushClip(destRect))
+ using (context.PushTransform(translate * scale))
+ {
+ Drawing.Draw(context);
+ }
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/IconGenerators.Avalonia/UniversalIconStyles.axaml b/IconGenerators.Avalonia/UniversalIconStyles.axaml
new file mode 100644
index 0000000..b8c7b15
--- /dev/null
+++ b/IconGenerators.Avalonia/UniversalIconStyles.axaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/IconGenerators.Avalonia/UniversalIconStyles.axaml.cs b/IconGenerators.Avalonia/UniversalIconStyles.axaml.cs
new file mode 100644
index 0000000..ce3d011
--- /dev/null
+++ b/IconGenerators.Avalonia/UniversalIconStyles.axaml.cs
@@ -0,0 +1,19 @@
+using System;
+using Avalonia.Markup.Xaml;
+using Avalonia.Styling;
+using Dummy.Icons.Avalonia;
+
+namespace IconGenerators.Avalonia;
+///
+/// Styles for Dummy.Icons.Avalonia library
+///
+public sealed class UniversalIconStyles : Styles {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The parent's service provider.
+ public UniversalIconStyles(IServiceProvider? serviceProvider) {
+ // DummyIconsUtils.InitializeGeometryParser();
+ AvaloniaXamlLoader.Load(serviceProvider, this);
+ }
+}
diff --git a/build/Meta/ExtensionMethods.cs b/IconGenerators/Common/ExtensionMethods.cs
similarity index 96%
rename from build/Meta/ExtensionMethods.cs
rename to IconGenerators/Common/ExtensionMethods.cs
index ede8d66..4e85dce 100644
--- a/build/Meta/ExtensionMethods.cs
+++ b/IconGenerators/Common/ExtensionMethods.cs
@@ -1,6 +1,6 @@
using System.Text.RegularExpressions;
-namespace Meta {
+namespace IconGenerators.Common {
internal static class ExtensionMethods {
///
/// Separates the input words with underscore
diff --git a/build/Meta/IconInfo.cs b/IconGenerators/Common/IconInfo.cs
similarity index 84%
rename from build/Meta/IconInfo.cs
rename to IconGenerators/Common/IconInfo.cs
index 5a10597..106c17c 100644
--- a/build/Meta/IconInfo.cs
+++ b/IconGenerators/Common/IconInfo.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace Meta;
+namespace IconGenerators.Common;
public class IconInfo {
public string Name { get; set; }
diff --git a/IconGenerators/DummyIconDataProvider.cs b/IconGenerators/DummyIconDataProvider.cs
new file mode 100644
index 0000000..893cb15
--- /dev/null
+++ b/IconGenerators/DummyIconDataProvider.cs
@@ -0,0 +1,113 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using IconGenerators.Avalonia;
+
+namespace IconGenerators;
+
+
+///
+/// Provides access to the Material Design icons data and its geometry.
+///
+public partial class DummyIconDataProvider : IIconProvider
+{
+ private static DummyIconDataProvider _instance = new();
+ private static Func? _parser;
+
+ ///
+ /// Gets the cache singleton for the icons. The cache is used to store the parsed icons to avoid parsing them multiple times.
+ ///
+ private static Dictionary? _cache = new();
+
+ ///
+ /// Gets the cache for the icons. The cache is used to store the parsed icons to avoid parsing them multiple times.
+ ///
+ public static IReadOnlyDictionary? Cache => _cache;
+
+ ///
+ /// Gets or sets the singleton instance of this provider
+ ///
+ public static DummyIconDataProvider Instance
+ {
+ get => _instance;
+ set
+ {
+ _instance = value ?? throw new ArgumentNullException(nameof(value));
+ ClearCache();
+ }
+ }
+
+ ///
+ /// Disables the cache for the icons.
+ ///
+ public static void DisableCache()
+ {
+ _cache = null;
+ }
+
+ ///
+ /// Clears the cache for the icons.
+ ///
+ public static void ClearCache()
+ {
+ _cache?.Clear();
+ }
+
+ ///
+ /// Initializes the geometry parser with the specified parsing function.
+ ///
+ /// This method sets the parser function to be used for geometry parsing operations. If the
+ /// parser has already been initialized, subsequent calls to this method will have no effect.
+ /// A function that takes a string input and returns an object representing the parsed geometry. This parameter
+ /// cannot be null.
+ public static void InitializeGeometryParser(Func parser) => _parser ??= parser;
+
+ ///
+ /// Gets the geometry for the specified icon using the
+ ///
+ /// The icon kind
+ /// SVG path for target icon kind
+ public static T Get(DummyIconKind kind) where T : class
+ {
+ if (_cache?.TryGetValue(kind, out var value) is true)
+ {
+ return value as T ?? throw new InvalidOperationException(
+ "Invalid type for icon kind. Check that you are requesting the correct geometry type.");
+ }
+
+ if (_parser is null)
+ {
+ throw new InvalidOperationException(
+ "Geometry parser not initialized. Call InitializeGeometryParser first.");
+ }
+
+ var result = _parser(GetData(kind)) as T ?? throw new InvalidOperationException(
+ "Parser returns a wrong type. Check that you are requesting the correct geometry type.");
+
+ if (_cache != null)
+ _cache[kind] = result;
+
+ return result;
+ }
+
+ ///
+ /// Gets the data for the specified icon using the
+ ///
+ /// The icon kind
+ /// SVG path for target icon kind
+ public static string GetData(DummyIconKind kind) => Instance.ProvideData(kind);
+
+ ///
+ /// Provides the data for the specified icon kind
+ ///
+ /// The icon kind
+ /// SVG path for target icon kind
+ public virtual partial string ProvideData(DummyIconKind kind);
+
+ public string ProvideData(string kindstring)
+ {
+ // Using Enum.Parse (throws if invalid)
+ DummyIconKind kind = (DummyIconKind)Enum.Parse(typeof(DummyIconKind), kindstring);
+ return ProvideData(kind);
+ }
+}
diff --git a/IconGenerators/DummyIconKind.cs b/IconGenerators/DummyIconKind.cs
new file mode 100644
index 0000000..cf842a0
--- /dev/null
+++ b/IconGenerators/DummyIconKind.cs
@@ -0,0 +1,6 @@
+namespace IconGenerators
+{
+ public enum DummyIconKind
+ {
+ }
+}
diff --git a/IconGenerators/Feather/FeatherDownloader.cs b/IconGenerators/Feather/FeatherDownloader.cs
new file mode 100644
index 0000000..35a2e79
--- /dev/null
+++ b/IconGenerators/Feather/FeatherDownloader.cs
@@ -0,0 +1,133 @@
+using IconGenerators.Common;
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Text;
+using System.Text.Json;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Xml;
+
+namespace IconGenerators.Feather
+{
+ public class FeatherDownloader : IIconPackGenerator
+ {
+ public string Name => "Feather";
+ public async Task> FetchIconData()
+ {
+ var tagsUrl = "https://raw.githubusercontent.com/feathericons/feather/refs/heads/main/src/tags.json";
+ using var http = new HttpClient();
+ var json = await http.GetStringAsync(tagsUrl);
+
+ // tags.json is a dictionary: { "iconName": [ "tag1", "tag2", ... ], ... }
+ var icons = JsonSerializer.Deserialize>>(json);
+ if (icons == null)
+ {
+ Console.WriteLine("Failed to parse icon names.");
+ return null;
+ }
+
+ var iconlist = new List();
+ foreach (var iconName in icons.Keys)
+ {
+ var svgUrl = $"https://raw.githubusercontent.com/feathericons/feather/refs/heads/main/icons/{iconName}.svg";
+ try
+ {
+ var svgData = await http.GetStringAsync(svgUrl);
+ var pathData = ExtractPathLikeData(svgData);
+ if (string.IsNullOrEmpty(pathData))
+ continue;
+
+ iconlist.Add(new IconInfo { Name = IconPackGenerator.ToPascalCase(iconName), Data = pathData });
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Failed to fetch or parse SVG for {iconName}: {ex.Message}");
+ }
+ }
+
+ return iconlist;
+ }
+ private static string ExtractPathLikeData(string svg)
+ {
+ if (string.IsNullOrWhiteSpace(svg))
+ return string.Empty;
+
+ try
+ {
+ var xmlDoc = new XmlDocument();
+ xmlDoc.LoadXml(svg);
+
+ // Try
+ var pathNodes = xmlDoc.GetElementsByTagName("path");
+ if (pathNodes.Count > 0 && pathNodes[0].Attributes["d"] != null)
+ {
+ return pathNodes[0].Attributes["d"].Value;
+ }
+
+ // Try
+ var polylineNodes = xmlDoc.GetElementsByTagName("polyline");
+ if (polylineNodes.Count > 0 && polylineNodes[0].Attributes["points"] != null)
+ {
+ var points = polylineNodes[0].Attributes["points"].Value;
+ return PolylinePointsToPathData(points, close: false);
+ }
+
+ // Try
+ var polygonNodes = xmlDoc.GetElementsByTagName("polygon");
+ if (polygonNodes.Count > 0 && polygonNodes[0].Attributes["points"] != null)
+ {
+ var points = polygonNodes[0].Attributes["points"].Value;
+ return PolylinePointsToPathData(points, close: true);
+ }
+
+ // Try
+ var circleNodes = xmlDoc.GetElementsByTagName("circle");
+ if (circleNodes.Count > 0)
+ {
+ var cx = circleNodes[0].Attributes["cx"]?.Value;
+ var cy = circleNodes[0].Attributes["cy"]?.Value;
+ var r = circleNodes[0].Attributes["r"]?.Value;
+ if (cx != null && cy != null && r != null && double.TryParse(cx, out var cxVal) && double.TryParse(cy, out var cyVal) && double.TryParse(r, out var rVal))
+ {
+ // XAML Path for a circle: M cx+r,cy A r,r 0 1 0 cx-r,cy A r,r 0 1 0 cx+r,cy
+ // This draws a full circle using two arcs
+ var startX = cxVal + rVal;
+ var startY = cyVal;
+ var endX = cxVal - rVal;
+ var endY = cyVal;
+ return $"M {startX},{startY} A {rVal},{rVal} 0 1 0 {endX},{endY} A {rVal},{rVal} 0 1 0 {startX},{startY}";
+ }
+ }
+ }
+ catch
+ {
+ // Ignore malformed SVG
+ }
+ return string.Empty;
+ }
+
+ private static string PolylinePointsToPathData(string points, bool close)
+ {
+ // points: "x1 y1 x2 y2 x3 y3 ..."
+ var matches = Regex.Matches(points, @"-?\d*\.?\d+");
+ if (matches.Count < 2) return string.Empty;
+
+ var coords = new List();
+ foreach (Match m in matches)
+ coords.Add(m.Value);
+
+ if (coords.Count < 4) return string.Empty;
+
+ var sb = new System.Text.StringBuilder();
+ sb.Append($"M {coords[0]},{coords[1]}");
+ for (int i = 2; i < coords.Count; i += 2)
+ {
+ sb.Append($" L {coords[i]},{coords[i + 1]}");
+ }
+ if (close)
+ sb.Append(" Z");
+ return sb.ToString();
+ }
+ }
+}
diff --git a/IconGenerators/FontAwesome/FontAwesomeDownloader.cs b/IconGenerators/FontAwesome/FontAwesomeDownloader.cs
new file mode 100644
index 0000000..8fcca45
--- /dev/null
+++ b/IconGenerators/FontAwesome/FontAwesomeDownloader.cs
@@ -0,0 +1,66 @@
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Text.Json;
+using System.Threading.Tasks;
+using System.Xml;
+using IconGenerators.Common;
+
+namespace IconGenerators.FontAwesome;
+
+
+public class FontAwesomeDownloader : IIconPackGenerator
+{
+ public string Name => "FontAwesome";
+ public async Task> FetchIconData()
+ {
+ var url = "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/refs/heads/master/metadata/icons.json";
+ using var httpClient = new HttpClient();
+ var json = await httpClient.GetStringAsync(url);
+
+ using var doc = JsonDocument.Parse(json);
+ var icons = new List();
+
+ foreach (var iconProperty in doc.RootElement.EnumerateObject())
+ {
+ var name = iconProperty.Name;
+ if (iconProperty.Value.TryGetProperty("svg", out var svgElement))
+ {
+ foreach (var styleProperty in svgElement.EnumerateObject())
+ {
+ if (styleProperty.Value.TryGetProperty("raw", out var rawSvgElement))
+ {
+ var rawSvg = rawSvgElement.GetString();
+ var xamlPath = ConvertSvgToXamlPath(rawSvg);
+ icons.Add(new IconInfo { Name = IconPackGenerator.ToPascalCase(name), Data = xamlPath });
+ break;
+ }
+ }
+ }
+ }
+
+ return icons;
+ }
+
+ private static string ConvertSvgToXamlPath(string svg)
+ {
+ if (string.IsNullOrWhiteSpace(svg))
+ return string.Empty;
+
+ try
+ {
+ var xmlDoc = new XmlDocument();
+ xmlDoc.LoadXml(svg);
+
+ var pathNode = xmlDoc.GetElementsByTagName("path");
+ if (pathNode.Count > 0 && pathNode[0].Attributes["d"] != null)
+ {
+ return pathNode[0].Attributes["d"].Value;
+ }
+ }
+ catch
+ {
+ // Ignore malformed SVG
+ }
+ return string.Empty;
+ }
+}
\ No newline at end of file
diff --git a/IconGenerators/IIconPackGenerator.cs b/IconGenerators/IIconPackGenerator.cs
new file mode 100644
index 0000000..bb7cf13
--- /dev/null
+++ b/IconGenerators/IIconPackGenerator.cs
@@ -0,0 +1,13 @@
+using IconGenerators.Common;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace IconGenerators
+{
+ public interface IIconPackGenerator
+ {
+ public string Name { get; }
+ public Task> FetchIconData();
+ }
+}
diff --git a/IconGenerators/IconGenerators.csproj b/IconGenerators/IconGenerators.csproj
new file mode 100644
index 0000000..8c60107
--- /dev/null
+++ b/IconGenerators/IconGenerators.csproj
@@ -0,0 +1,30 @@
+
+
+
+ NET10.0
+ 12
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/IconGenerators/IconPackGenerator.cs b/IconGenerators/IconPackGenerator.cs
new file mode 100644
index 0000000..fdd7345
--- /dev/null
+++ b/IconGenerators/IconPackGenerator.cs
@@ -0,0 +1,167 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using IconGenerators.Common;
+using Nuke.Common.IO;
+
+namespace IconGenerators;
+
+public static class IconPackGenerator
+{
+ public static void Generate(IEnumerable icons, AbsolutePath dir, string iconPackName)
+ {
+ var targetDir = dir / "out" / iconPackName + ".Icons";
+
+ // Copy and adapt IconGeneratorTemplate.Avalonia project
+ var templateDir = dir / "IconGeneratorTemplate.Avalonia";
+ var targetProjectDir = dir / "out" / (iconPackName + ".Icons.Avalonia");
+ if (!Directory.Exists(targetProjectDir))
+ Directory.CreateDirectory(targetProjectDir);
+
+ foreach (var file in Directory.GetFiles(templateDir, "*", SearchOption.TopDirectoryOnly))
+ {
+ var fileName = Path.GetFileName(file);
+ if (fileName == "obj" || fileName.EndsWith(".user")) continue;
+
+ var fileContent = File.ReadAllText(file);
+ // Replace Dummy, namespace, and using IconGenerators;
+ fileContent = fileContent.Replace("Dummy", iconPackName);
+ fileContent = Regex.Replace(fileContent, @"namespace\\s+Dummy\\.Icons\\.Avalonia", $"namespace {iconPackName}.Icons.Avalonia");
+ fileContent = Regex.Replace(fileContent, @"using\s+IconGenerators;", $"using {iconPackName};");
+
+ // If this is the .csproj file, update the project reference
+ if (fileName.EndsWith(".csproj"))
+ {
+ // Update IconGenerators.Avalonia reference to go up two directories
+ fileContent = Regex.Replace(
+ fileContent,
+ @"",
+ "");
+
+
+ // Update IconGenerators reference as before
+ fileContent = Regex.Replace(
+ fileContent,
+ @"",
+ $"");
+
+
+ // Ensure all remaining 'Dummy' are replaced
+ fileContent = fileContent.Replace("Dummy", iconPackName);
+ }
+
+ var newFileName = fileName.Replace("Dummy", iconPackName).Replace("IconGeneratorTemplate.Avalonia", iconPackName + ".Icons.Avalonia");
+ var newFilePath = Path.Combine(targetProjectDir, newFileName);
+ File.WriteAllText(newFilePath, fileContent);
+ }
+
+ var enumTypeName = $"{iconPackName}IconKind";
+ var dataProviderTypeName = $"{iconPackName}IconDataProvider";
+
+ if (!Directory.Exists(targetDir))
+ Directory.CreateDirectory(targetDir);
+ // Copy and adapt DummyIconDataProvider
+ var assembly = typeof(IconPackGenerator).Assembly;
+ var resourceName = assembly.GetManifestResourceNames()
+ .FirstOrDefault(n => n.EndsWith("DummyIconDataProvider.cs"));
+ if (resourceName != null)
+ {
+ using var stream = assembly.GetManifestResourceStream(resourceName);
+ using var reader = new StreamReader(stream);
+ var dummyProviderContent = reader.ReadToEnd();
+ var replacedContent = dummyProviderContent.Replace("Dummy", iconPackName);
+ // Replace the namespace line with the correct namespace
+ replacedContent = System.Text.RegularExpressions.Regex.Replace(
+ replacedContent,
+ @"^namespace\s+.*$",
+ $"namespace {iconPackName}.Icons;",
+ System.Text.RegularExpressions.RegexOptions.Multiline);
+ var outputProviderPath = targetDir / $"{iconPackName}IconDataProvider.cs";
+ File.WriteAllText(outputProviderPath, replacedContent);
+ }
+ var enumOutputFile = targetDir / $"{enumTypeName}.cs";
+ var pathDataOutputFile = targetDir / $"{dataProviderTypeName}.PathData.cs";
+ var csprojFile = targetDir / $"{iconPackName}.Icons.csproj";
+
+ // Generate enum file
+ var enumSb = new StringBuilder();
+ enumSb.AppendLine($"namespace {iconPackName}.Icons;");
+ enumSb.AppendLine("/// ******************************************");
+ enumSb.AppendLine("/// This code is auto generated. Do not amend.");
+ enumSb.AppendLine("/// ******************************************");
+ enumSb.AppendLine("/// ");
+ enumSb.AppendLine("/// List of available icon kinds");
+ enumSb.AppendLine("/// ");
+ enumSb.AppendLine($"public enum {enumTypeName}");
+ enumSb.AppendLine("{");
+ foreach (var icon in icons)
+ enumSb.AppendLine($" {icon.Name},");
+ enumSb.AppendLine("}");
+ File.WriteAllText(enumOutputFile, enumSb.ToString());
+
+ // Generate path data provider file
+ var pathSb = new StringBuilder();
+ pathSb.AppendLine("using System.Runtime.CompilerServices;");
+ pathSb.AppendLine();
+ pathSb.AppendLine($"namespace {iconPackName}.Icons;");
+ pathSb.AppendLine("/// ******************************************");
+ pathSb.AppendLine("/// This code is auto generated. Do not amend.");
+ pathSb.AppendLine("/// ******************************************");
+ pathSb.AppendLine($"public partial class {dataProviderTypeName}");
+ pathSb.AppendLine("{");
+ pathSb.AppendLine(" [MethodImpl(MethodImplOptions.AggressiveInlining)]");
+ pathSb.AppendLine($" public virtual partial string ProvideData({enumTypeName} kind)");
+ pathSb.AppendLine(" => kind switch");
+ pathSb.AppendLine(" {");
+ foreach (var icon in icons)
+ {
+ var escapedData = icon.Data.Replace("\"", "\"\"");
+ pathSb.AppendLine($" {enumTypeName}.{icon.Name} => @\"{escapedData}\",");
+ }
+ pathSb.AppendLine(" _ => string.Empty");
+ pathSb.AppendLine(" };");
+ pathSb.AppendLine("}");
+ File.WriteAllText(pathDataOutputFile, pathSb.ToString());
+
+ // Generate .csproj file
+ var csprojSb = new StringBuilder();
+ csprojSb.AppendLine("");
+ csprojSb.AppendLine(" ");
+ csprojSb.AppendLine(" net10.0");
+ csprojSb.AppendLine(" enable");
+ csprojSb.AppendLine(" latest");
+ csprojSb.AppendLine(" ");
+ csprojSb.AppendLine("");
+ csprojSb.AppendLine($"");
+ csprojSb.AppendLine("");
+ csprojSb.AppendLine("");
+
+
+
+
+
+ File.WriteAllText(csprojFile, csprojSb.ToString());
+ }
+
+ public static string ToPascalCase(this string name)
+ {
+ // Split on any non-alphanumeric character (including hyphens) to remove them
+ var parts = Regex.Split(name, @"[^a-zA-Z0-9]+");
+ var pascal = new StringBuilder();
+ foreach (var part in parts)
+ {
+ if (string.IsNullOrEmpty(part))
+ continue;
+ pascal.Append(char.ToUpperInvariant(part[0]));
+ if (part.Length > 1)
+ pascal.Append(part.Substring(1));
+ }
+ var result = pascal.ToString();
+ if (!string.IsNullOrEmpty(result) && char.IsDigit(result[0]))
+ result = "_" + result;
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/IconGenerators/LineIcons/LineIconsDownloader.cs b/IconGenerators/LineIcons/LineIconsDownloader.cs
new file mode 100644
index 0000000..7d0b623
--- /dev/null
+++ b/IconGenerators/LineIcons/LineIconsDownloader.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Text.Json;
+using System.Threading.Tasks;
+using System.Xml;
+using IconGenerators.Common;
+
+namespace IconGenerators.LineIcons
+{
+ public class LineIconsDownloader : IIconPackGenerator
+ {
+ public string Name => "LineIcon";
+
+ public async Task> FetchIconData()
+ {
+ var icons = new List();
+ try
+ {
+ var url = "https://raw.githubusercontent.com/LineiconsHQ/Lineicons/main/assets/icon-fonts/unicodesMap.json";
+ using var http = new HttpClient();
+ var json = await http.GetStringAsync(url);
+
+ // Parse the JSON as a dictionary
+ var icons2 = JsonSerializer.Deserialize>(json);
+
+ if (icons2 == null)
+ {
+ Console.WriteLine("Failed to parse icon names.");
+ return icons;
+ }
+
+ foreach (var iconName in icons2.Keys)
+ {
+ // Remove "lni-" prefix if present
+ var id = iconName.StartsWith("lni-") ? iconName.Substring(4) : iconName;
+ var svgUrl = $"https://raw.githubusercontent.com/LineiconsHQ/Lineicons/refs/heads/main/assets/svgs/regular/{id}.svg";
+
+ try
+ {
+ var svgData = await http.GetStringAsync(svgUrl);
+ var pathData = ExtractPathData(svgData);
+ var info = new IconInfo
+ {
+ Name = id.ToPascalCase(),
+ Data = pathData,
+ };
+ if (info.Name != null)
+ icons.Add(info);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Failed to fetch or parse SVG for {iconName}: {ex.Message}");
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Error parsing JSON: {ex.Message}");
+ }
+ return icons;
+ }
+
+ private static string ExtractPathData(string svg)
+ {
+ if (string.IsNullOrWhiteSpace(svg))
+ return string.Empty;
+
+ try
+ {
+ var xmlDoc = new XmlDocument();
+ xmlDoc.LoadXml(svg);
+
+ var pathNode = xmlDoc.GetElementsByTagName("path");
+ if (pathNode.Count > 0 && pathNode[0].Attributes["d"] != null)
+ {
+ return pathNode[0].Attributes["d"].Value;
+ }
+ }
+ catch
+ {
+ // Ignore malformed SVG
+ }
+ return string.Empty;
+ }
+ }
+}
diff --git a/IconGenerators/Lucide/LucideDownloader.cs b/IconGenerators/Lucide/LucideDownloader.cs
new file mode 100644
index 0000000..62b35a8
--- /dev/null
+++ b/IconGenerators/Lucide/LucideDownloader.cs
@@ -0,0 +1,149 @@
+using IconGenerators.Common;
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Text;
+using System.Text.Json;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Xml;
+
+namespace IconGenerators.Lucide
+{
+ public class LucideDownloader : IIconPackGenerator
+ {
+ public string Name => "Lucide";
+
+ public async Task> FetchIconData()
+ {
+ var icons = new List();
+ var apiUrl = "https://api.github.com/repos/lucide-icons/lucide/contents/icons";
+ using var http = new HttpClient();
+ http.DefaultRequestHeaders.UserAgent.ParseAdd("CSharpApp"); // GitHub API requires a User-Agent
+
+ var json = await http.GetStringAsync(apiUrl);
+ var files = JsonSerializer.Deserialize>(json);
+
+ if (files == null)
+ {
+ Console.WriteLine("Failed to get directory info from GitHub API.");
+ return null;
+ }
+
+ Console.WriteLine("Lucide icon names and SVG path data:");
+ foreach (var file in files)
+ {
+ if (file.type == "file" && file.name.EndsWith(".svg", StringComparison.OrdinalIgnoreCase))
+ {
+ var iconName = file.name[..^4]; // Remove ".svg"
+ try
+ {
+ var svgData = await http.GetStringAsync(file.download_url);
+ var pathData = ExtractPathLikeData(svgData);
+
+ icons.Add(new IconInfo
+ {
+ Name = iconName.ToPascalCase(),
+ Data = pathData,
+ });
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Failed to fetch or parse SVG for {iconName}: {ex.Message}");
+ }
+ }
+ }
+
+ return icons;
+ }
+
+
+ public class GitHubFile
+ {
+ public string name { get; set; }
+ public string download_url { get; set; }
+ public string type { get; set; }
+ }
+
+ private static string ExtractPathLikeData(string svg)
+ {
+ if (string.IsNullOrWhiteSpace(svg))
+ return string.Empty;
+
+ try
+ {
+ var xmlDoc = new XmlDocument();
+ xmlDoc.LoadXml(svg);
+
+ // Try
+ var pathNodes = xmlDoc.GetElementsByTagName("path");
+ if (pathNodes.Count > 0 && pathNodes[0].Attributes["d"] != null)
+ {
+ return pathNodes[0].Attributes["d"].Value;
+ }
+
+ // Try
+ var polylineNodes = xmlDoc.GetElementsByTagName("polyline");
+ if (polylineNodes.Count > 0 && polylineNodes[0].Attributes["points"] != null)
+ {
+ var points = polylineNodes[0].Attributes["points"].Value;
+ return PolylinePointsToPathData(points, close: false);
+ }
+
+ // Try
+ var polygonNodes = xmlDoc.GetElementsByTagName("polygon");
+ if (polygonNodes.Count > 0 && polygonNodes[0].Attributes["points"] != null)
+ {
+ var points = polygonNodes[0].Attributes["points"].Value;
+ return PolylinePointsToPathData(points, close: true);
+ }
+
+ // Try
+ var circleNodes = xmlDoc.GetElementsByTagName("circle");
+ if (circleNodes.Count > 0)
+ {
+ var cx = circleNodes[0].Attributes["cx"]?.Value;
+ var cy = circleNodes[0].Attributes["cy"]?.Value;
+ var r = circleNodes[0].Attributes["r"]?.Value;
+ if (cx != null && cy != null && r != null && double.TryParse(cx, out var cxVal) && double.TryParse(cy, out var cyVal) && double.TryParse(r, out var rVal))
+ {
+ var startX = cxVal + rVal;
+ var startY = cyVal;
+ var endX = cxVal - rVal;
+ var endY = cyVal;
+ return $"M {startX},{startY} A {rVal},{rVal} 0 1 0 {endX},{endY} A {rVal},{rVal} 0 1 0 {startX},{startY}";
+ }
+ }
+ }
+ catch
+ {
+ // Ignore malformed SVG
+ }
+ return string.Empty;
+ }
+
+ private static string PolylinePointsToPathData(string points, bool close)
+ {
+ var matches = Regex.Matches(points, @"-?\d*\.?\d+");
+ if (matches.Count < 2) return string.Empty;
+
+ var coords = new List();
+ foreach (Match m in matches)
+ coords.Add(m.Value);
+
+ if (coords.Count < 4) return string.Empty;
+
+ var sb = new System.Text.StringBuilder();
+ sb.Append($"M {coords[0]},{coords[1]}");
+ for (int i = 2; i < coords.Count; i += 2)
+ {
+ sb.Append($" L {coords[i]},{coords[i + 1]}");
+ }
+ if (close)
+ sb.Append(" Z");
+ return sb.ToString();
+ }
+
+
+ }
+}
diff --git a/build/Generators/PathDataGenerators/DictionaryStringGenerator.cs b/IconGenerators/Material/DictionaryStringGenerator.cs
similarity index 97%
rename from build/Generators/PathDataGenerators/DictionaryStringGenerator.cs
rename to IconGenerators/Material/DictionaryStringGenerator.cs
index c6287fa..9dd667b 100644
--- a/build/Generators/PathDataGenerators/DictionaryStringGenerator.cs
+++ b/IconGenerators/Material/DictionaryStringGenerator.cs
@@ -1,11 +1,10 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
-using Meta;
+using IconGenerators.Common;
using Nuke.Common.IO;
using Serilog;
-
-namespace Generators.PathDataGenerators;
+namespace IconGenerators.Material;
public class DictionaryStringGenerator {
public static void Write(AbsolutePath destinationPath, IEnumerable iconInfos) {
diff --git a/build/Meta/MaterialIconsMetaTools.cs b/IconGenerators/Material/MaterialDownloader.cs
similarity index 65%
rename from build/Meta/MaterialIconsMetaTools.cs
rename to IconGenerators/Material/MaterialDownloader.cs
index ed4f3ff..d664ea5 100644
--- a/build/Meta/MaterialIconsMetaTools.cs
+++ b/IconGenerators/Material/MaterialDownloader.cs
@@ -1,35 +1,46 @@
-using System;
+using IconGenerators.Common;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
-namespace Meta;
+namespace IconGenerators.Material;
+
+public class MaterialDownloader : IIconPackGenerator
+{
-public static class MaterialIconsMetaTools {
static readonly HttpClient HttpClient = new();
const string MaterialIconsFetchApi = "https://dev.materialdesignicons.com/api/package/38EF63D0-4744-11E4-B3CF-842B2B6CFE1B";
- public static async Task> GetIcons() {
+ public string Name => "Material";
+
+ public async Task> FetchIconData()
+ {
var dataStream = await HttpClient.GetStreamAsync(MaterialIconsFetchApi);
var dataNode = await JsonSerializer.DeserializeAsync(dataStream, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
var icons = dataNode.Icons.Select(ToIconInfo);
var iconsByName = new Dictionary(StringComparer.OrdinalIgnoreCase);
- foreach (var icon in icons.Where(icon => !iconsByName.ContainsKey(icon.Name))) {
+ foreach (var icon in icons.Where(icon => !iconsByName.ContainsKey(icon.Name)))
+ {
iconsByName.Add(icon.Name, icon);
}
//Clean up aliases to avoid naming collisions
var seenAliases = new HashSet(StringComparer.OrdinalIgnoreCase);
- foreach (var icon in iconsByName.Values) {
+ foreach (var icon in iconsByName.Values)
+ {
seenAliases.Add(icon.Name);
}
- foreach (var icon in iconsByName.Values) {
- for (var i = icon.Aliases.Count - 1; i >= 0; i--) {
+ foreach (var icon in iconsByName.Values)
+ {
+ for (var i = icon.Aliases.Count - 1; i >= 0; i--)
+ {
var alias = icon.Aliases[i];
- if (iconsByName.ContainsKey(alias) || !IsValidIdentifier(alias) || seenAliases.Add(alias) == false) {
+ if (iconsByName.ContainsKey(alias) || !IsValidIdentifier(alias) || seenAliases.Add(alias) == false)
+ {
icon.Aliases.RemoveAt(i);
}
}
@@ -37,19 +48,25 @@ public static async Task> GetIcons() {
return iconsByName.Values.OrderBy(x => x.Name);
- static bool IsValidIdentifier(string identifier) {
+ static bool IsValidIdentifier(string identifier)
+ {
return identifier?.Length > 0 && (char.IsLetter(identifier[0]) || identifier[0] == '_');
}
}
- static IconInfo ToIconInfo(MaterialIconInfo info) {
- return new IconInfo {
- Aliases = info.Aliases.Select(alias => alias.Name.Underscore().Pascalize()).ToList(),
- Name = info.Name.Underscore().Pascalize(),
+ static IconInfo ToIconInfo(MaterialIconInfo info)
+ {
+ return new IconInfo
+ {
+ Aliases = info.Aliases.Select(alias => alias.Name.Underscore().Pascalize()).ToList(),
+ Name = info.Name.Underscore().Pascalize(),
Data = info.Data
};
}
- internal class MetaMaterialIcons {
+
+
+ internal class MetaMaterialIcons
+ {
public List Icons { get; set; }
}
}
diff --git a/build/Meta/MaterialIconInfo.cs b/IconGenerators/Material/MaterialIconInfo.cs
similarity index 92%
rename from build/Meta/MaterialIconInfo.cs
rename to IconGenerators/Material/MaterialIconInfo.cs
index c6faed1..eb78f91 100644
--- a/build/Meta/MaterialIconInfo.cs
+++ b/IconGenerators/Material/MaterialIconInfo.cs
@@ -2,7 +2,7 @@
using System.Linq;
using System.Text.Json.Serialization;
-namespace Meta {
+namespace IconGenerators.Material {
public class MaterialIconInfo {
public string Name { get; set; }
diff --git a/build/Meta/MaterialIconInfoAlias.cs b/IconGenerators/Material/MaterialIconInfoAlias.cs
similarity index 68%
rename from build/Meta/MaterialIconInfoAlias.cs
rename to IconGenerators/Material/MaterialIconInfoAlias.cs
index ac297d1..7c649c8 100644
--- a/build/Meta/MaterialIconInfoAlias.cs
+++ b/IconGenerators/Material/MaterialIconInfoAlias.cs
@@ -1,4 +1,4 @@
-namespace Meta;
+namespace IconGenerators.Material;
public class MaterialIconInfoAlias {
public string Name { get; set; }
diff --git a/build/Generators/MaterialIconKindEnumGenerator.cs b/IconGenerators/Material/MaterialIconKindEnumGenerator.cs
similarity index 96%
rename from build/Generators/MaterialIconKindEnumGenerator.cs
rename to IconGenerators/Material/MaterialIconKindEnumGenerator.cs
index 201d6f5..1b54dad 100644
--- a/build/Generators/MaterialIconKindEnumGenerator.cs
+++ b/IconGenerators/Material/MaterialIconKindEnumGenerator.cs
@@ -2,11 +2,11 @@
using System.Collections.ObjectModel;
using System.IO;
using System.Text;
-using Meta;
+using IconGenerators.Common;
using Nuke.Common.IO;
using Serilog;
-namespace Generators;
+namespace IconGenerators.Material;
public static class MaterialIconKindEnumGenerator {
public static void Write(AbsolutePath destinationPath, IEnumerable iconInfos) {
diff --git a/build/Meta/MaterialIconUser.cs b/IconGenerators/Material/MaterialIconUser.cs
similarity index 76%
rename from build/Meta/MaterialIconUser.cs
rename to IconGenerators/Material/MaterialIconUser.cs
index e76eb30..2dbd4e1 100644
--- a/build/Meta/MaterialIconUser.cs
+++ b/IconGenerators/Material/MaterialIconUser.cs
@@ -1,6 +1,4 @@
-
-
-namespace Meta {
+namespace IconGenerators.Material {
public class MaterialIconUser
{
public string Id { get; set; }
diff --git a/build/Generators/PathDataGenerators/SwitchRegularStringGenerator.cs b/IconGenerators/Material/SwitchRegularStringGenerator.cs
similarity index 96%
rename from build/Generators/PathDataGenerators/SwitchRegularStringGenerator.cs
rename to IconGenerators/Material/SwitchRegularStringGenerator.cs
index c665122..5999789 100644
--- a/build/Generators/PathDataGenerators/SwitchRegularStringGenerator.cs
+++ b/IconGenerators/Material/SwitchRegularStringGenerator.cs
@@ -1,11 +1,12 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
-using Meta;
+using IconGenerators.Common;
using Nuke.Common.IO;
using Serilog;
-namespace Generators.PathDataGenerators;
+
+namespace IconGenerators.Material;
public class SwitchRegularStringGenerator {
public static void Write(AbsolutePath destinationPath, IEnumerable iconInfos) {
diff --git a/IconPackGenerators.slnx b/IconPackGenerators.slnx
new file mode 100644
index 0000000..c80a2ad
--- /dev/null
+++ b/IconPackGenerators.slnx
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index da9f97e..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2021 SKProCH
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/Material.Icons.Avalonia.Demo/.gitignore b/Material.Icons.Avalonia.Demo/.gitignore
deleted file mode 100644
index dbe7c8f..0000000
--- a/Material.Icons.Avalonia.Demo/.gitignore
+++ /dev/null
@@ -1,337 +0,0 @@
-## Ignore Visual Studio temporary files, build results, and
-## files generated by popular Visual Studio add-ons.
-##
-## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
-
-## Visual Studio Code specific files and folder
-.vscode/*
-!.vscode/settings.json
-!.vscode/tasks.json
-!.vscode/launch.json
-!.vscode/extensions.jsons
-
-# User-specific files
-*.suo
-*.user
-*.userosscache
-*.sln.docstates
-
-# User-specific files (MonoDevelop/Xamarin Studio)
-*.userprefs
-
-# Build results
-[Dd]ebug/
-[Dd]ebugPublic/
-[Rr]elease/
-[Rr]eleases/
-x64/
-x86/
-bld/
-[Bb]in/
-[Oo]bj/
-[Ll]og/
-
-# Visual Studio 2015/2017 cache/options directory
-.vs/
-# Uncomment if you have tasks that create the project's static files in wwwroot
-#wwwroot/
-
-# Visual Studio 2017 auto generated files
-Generated\ Files/
-
-# MSTest test Results
-[Tt]est[Rr]esult*/
-[Bb]uild[Ll]og.*
-
-# NUNIT
-*.VisualState.xml
-TestResult.xml
-
-# Build Results of an ATL Project
-[Dd]ebugPS/
-[Rr]eleasePS/
-dlldata.c
-
-# Benchmark Results
-BenchmarkDotNet.Artifacts/
-
-# .NET Core
-project.lock.json
-project.fragment.lock.json
-artifacts/
-**/Properties/launchSettings.json
-
-# StyleCop
-StyleCopReport.xml
-
-# Files built by Visual Studio
-*_i.c
-*_p.c
-*_i.h
-*.ilk
-*.meta
-*.obj
-*.iobj
-*.pch
-*.pdb
-*.ipdb
-*.pgc
-*.pgd
-*.rsp
-*.sbr
-*.tlb
-*.tli
-*.tlh
-*.tmp
-*.tmp_proj
-*.log
-*.vspscc
-*.vssscc
-.builds
-*.pidb
-*.svclog
-*.scc
-
-# Chutzpah Test files
-_Chutzpah*
-
-# Visual C++ cache files
-ipch/
-*.aps
-*.ncb
-*.opendb
-*.opensdf
-*.sdf
-*.cachefile
-*.VC.db
-*.VC.VC.opendb
-
-# Visual Studio profiler
-*.psess
-*.vsp
-*.vspx
-*.sap
-
-# Visual Studio Trace Files
-*.e2e
-
-# TFS 2012 Local Workspace
-$tf/
-
-# Guidance Automation Toolkit
-*.gpState
-
-# ReSharper is a .NET coding add-in
-_ReSharper*/
-*.[Rr]e[Ss]harper
-*.DotSettings.user
-
-# JustCode is a .NET coding add-in
-.JustCode
-
-# TeamCity is a build add-in
-_TeamCity*
-
-# DotCover is a Code Coverage Tool
-*.dotCover
-
-# AxoCover is a Code Coverage Tool
-.axoCover/*
-!.axoCover/settings.json
-
-# Visual Studio code coverage results
-*.coverage
-*.coveragexml
-
-# NCrunch
-_NCrunch_*
-.*crunch*.local.xml
-nCrunchTemp_*
-
-# MightyMoose
-*.mm.*
-AutoTest.Net/
-
-# Web workbench (sass)
-.sass-cache/
-
-# Installshield output folder
-[Ee]xpress/
-
-# DocProject is a documentation generator add-in
-DocProject/buildhelp/
-DocProject/Help/*.HxT
-DocProject/Help/*.HxC
-DocProject/Help/*.hhc
-DocProject/Help/*.hhk
-DocProject/Help/*.hhp
-DocProject/Help/Html2
-DocProject/Help/html
-
-# Click-Once directory
-publish/
-
-# Publish Web Output
-*.[Pp]ublish.xml
-*.azurePubxml
-# Note: Comment the next line if you want to checkin your web deploy settings,
-# but database connection strings (with potential passwords) will be unencrypted
-*.pubxml
-*.publishproj
-
-# Microsoft Azure Web App publish settings. Comment the next line if you want to
-# checkin your Azure Web App publish settings, but sensitive information contained
-# in these scripts will be unencrypted
-PublishScripts/
-
-# NuGet Packages
-*.nupkg
-# The packages folder can be ignored because of Package Restore
-**/[Pp]ackages/*
-# except build/, which is used as an MSBuild target.
-!**/[Pp]ackages/build/
-# Uncomment if necessary however generally it will be regenerated when needed
-#!**/[Pp]ackages/repositories.config
-# NuGet v3's project.json files produces more ignorable files
-*.nuget.props
-*.nuget.targets
-
-# Microsoft Azure Build Output
-csx/
-*.build.csdef
-
-# Microsoft Azure Emulator
-ecf/
-rcf/
-
-# Windows Store app package directories and files
-AppPackages/
-BundleArtifacts/
-Package.StoreAssociation.xml
-_pkginfo.txt
-*.appx
-
-# Visual Studio cache files
-# files ending in .cache can be ignored
-*.[Cc]ache
-# but keep track of directories ending in .cache
-!*.[Cc]ache/
-
-# Others
-ClientBin/
-~$*
-*~
-*.dbmdl
-*.dbproj.schemaview
-*.jfm
-*.pfx
-*.publishsettings
-orleans.codegen.cs
-
-# Including strong name files can present a security risk
-# (https://github.com/github/gitignore/pull/2483#issue-259490424)
-#*.snk
-
-# Since there are multiple workflows, uncomment next line to ignore bower_components
-# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
-#bower_components/
-
-# RIA/Silverlight projects
-Generated_Code/
-
-# Backup & report files from converting an old project file
-# to a newer Visual Studio version. Backup files are not needed,
-# because we have git ;-)
-_UpgradeReport_Files/
-Backup*/
-UpgradeLog*.XML
-UpgradeLog*.htm
-ServiceFabricBackup/
-*.rptproj.bak
-
-# SQL Server files
-*.mdf
-*.ldf
-*.ndf
-
-# Business Intelligence projects
-*.rdl.data
-*.bim.layout
-*.bim_*.settings
-*.rptproj.rsuser
-
-# Microsoft Fakes
-FakesAssemblies/
-
-# GhostDoc plugin setting file
-*.GhostDoc.xml
-
-# Node.js Tools for Visual Studio
-.ntvs_analysis.dat
-node_modules/
-
-# Visual Studio 6 build log
-*.plg
-
-# Visual Studio 6 workspace options file
-*.opt
-
-# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
-*.vbw
-
-# Visual Studio LightSwitch build output
-**/*.HTMLClient/GeneratedArtifacts
-**/*.DesktopClient/GeneratedArtifacts
-**/*.DesktopClient/ModelManifest.xml
-**/*.Server/GeneratedArtifacts
-**/*.Server/ModelManifest.xml
-_Pvt_Extensions
-
-# Paket dependency manager
-.paket/paket.exe
-paket-files/
-
-# FAKE - F# Make
-.fake/
-
-# JetBrains Rider
-.idea/
-*.sln.iml
-
-# CodeRush
-.cr/
-
-# Python Tools for Visual Studio (PTVS)
-__pycache__/
-*.pyc
-
-# Cake - Uncomment if you are using it
-# tools/**
-# !tools/packages.config
-
-# Tabs Studio
-*.tss
-
-# Telerik's JustMock configuration file
-*.jmconfig
-
-# BizTalk build output
-*.btp.cs
-*.btm.cs
-*.odx.cs
-*.xsd.cs
-
-# OpenCover UI analysis results
-OpenCover/
-
-# Azure Stream Analytics local run output
-ASALocalRun/
-
-# MSBuild Binary and Structured Log
-*.binlog
-
-# NVidia Nsight GPU debugger configuration file
-*.nvuser
-
-# MFractors (Xamarin productivity tool) working folder
-.mfractor/
diff --git a/Material.Icons.Avalonia.Demo/App.axaml b/Material.Icons.Avalonia.Demo/App.axaml
deleted file mode 100644
index 8efb736..0000000
--- a/Material.Icons.Avalonia.Demo/App.axaml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Material.Icons.Avalonia.Demo/App.axaml.cs b/Material.Icons.Avalonia.Demo/App.axaml.cs
deleted file mode 100644
index 97c2efc..0000000
--- a/Material.Icons.Avalonia.Demo/App.axaml.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Input.Platform;
-using Avalonia.Markup.Xaml;
-using Material.Icons.Avalonia.Demo.ViewModels;
-using Material.Icons.Avalonia.Demo.Views;
-
-namespace Material.Icons.Avalonia.Demo {
- public class App : Application {
- public override void Initialize() {
- AvaloniaXamlLoader.Load(this);
- }
-
- public static IClipboard Clipboard { get; private set; } = null!;
-
- public override void OnFrameworkInitializationCompleted() {
- if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) {
- desktop.MainWindow = new MainWindow {
- DataContext = new MainWindowViewModel(),
- };
-
- Clipboard = desktop.MainWindow.Clipboard!;
- }
-
- base.OnFrameworkInitializationCompleted();
- }
- }
-}
diff --git a/Material.Icons.Avalonia.Demo/Assets/avalonia-logo.ico b/Material.Icons.Avalonia.Demo/Assets/avalonia-logo.ico
deleted file mode 100644
index da8d49f..0000000
Binary files a/Material.Icons.Avalonia.Demo/Assets/avalonia-logo.ico and /dev/null differ
diff --git a/Material.Icons.Avalonia.Demo/Controls/SelectionWrapper.cs b/Material.Icons.Avalonia.Demo/Controls/SelectionWrapper.cs
deleted file mode 100644
index 3032a26..0000000
--- a/Material.Icons.Avalonia.Demo/Controls/SelectionWrapper.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using System;
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Controls.Metadata;
-
-namespace Material.Icons.Avalonia.Demo.Controls {
- [PseudoClasses("selectednow")]
- public class SelectionWrapper : ContentControl {
- public static readonly StyledProperty