Skip to content

Commit

Permalink
feat: Adds IconImage
Browse files Browse the repository at this point in the history
feat!: Uses the new `IconImage` inside the `Icon` template. This may affect styling.
  • Loading branch information
just-seba committed Dec 6, 2023
1 parent 355af64 commit ddd7bb4
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 126 deletions.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ Add `xmlns:i="https://github.com/projektanker/icons.avalonia"` to your view.
<i:Icon Value="fa-sync" Animation="Spin" />
```

**As an [Image](https://docs.avaloniaui.net/docs/reference/controls/image) source**
```xml
<Image>
<i:IconImage Value="fa-brands fa-anchor" Brush="(defaults to black)" />
</Image>
```

### Done

![Screenshot](/resources/demo.png?raw=true)
Expand Down Expand Up @@ -119,12 +126,12 @@ namespace Projektanker.Icons.Avalonia
```
and register it with the `IIconProviderContainer`:
```csharp
container.Register<MyCustomIconProvider>()
IconProvider.Current.Register<MyCustomIconProvider>()
```
or
```csharp
IIconProvider provider = new MyCustomIconProvider(/* custom ctor arguments */);
container.Register(provider);
IconProvider.Current.Register(provider);
```

The `IIconProvider.Prefix` property has to be unique within all registered providers. It is used to select the right provider. E.g. `FontAwesomeIconProvider`'s prefix is `fa`.
3 changes: 3 additions & 0 deletions demo/Demo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
<TargetFramework>net7.0</TargetFramework>
<AvaloniaNameGeneratorIsEnabled>false</AvaloniaNameGeneratorIsEnabled>
</PropertyGroup>
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia.Desktop" Version="11.0.2" />
<PackageReference Include="Avalonia.Diagnostics" Version="11.0.2" />
Expand Down
31 changes: 25 additions & 6 deletions demo/MainWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
<Style Selector="Button.disableByStyle">
<Setter Property="IsEnabled" Value="{Binding IsEnabled}" />
</Style>
<Style Selector="Image.icon">
<Setter Property="Grid.Column" Value="1" />
<Setter Property="Width" Value="24" />
<Setter Property="Height" Value="24" />
</Style>
</Window.Styles>

<DockPanel>
Expand All @@ -45,44 +50,58 @@
<!--Font Awesome-->
<Grid RowDefinitions="Auto, Auto, Auto, Auto, Auto" ColumnDefinitions="Auto, Auto, Auto" Margin="4">
<TextBlock Text="FontAwesome" Grid.Row="0" Grid.ColumnSpan="2" Classes="h1" />
<!--GitHub-->

<!--github-->
<TextBlock Text="fa-brands fa-github" Grid.Row="1" />
<i:Icon Value="fa-brands fa-github" Grid.Row="1" Foreground="BlueViolet" />
<Button i:Attached.Icon="fa-brands fa-github" Grid.Row="1" Foreground="BlueViolet" Classes="disableByStyle" />

<!--Address Card-->
<!--solid address-card"-->
<TextBlock Text="fa-solid fa-address-card" Grid.Row="2" />
<i:Icon Value="fa-solid fa-address-card" Grid.Row="2" />
<Button i:Attached.Icon="fa-solid fa-address-card" Grid.Row="2" Classes="disableByStyle" />

<!--regular address-card"-->
<TextBlock Text="fa-regular fa-address-card" Grid.Row="3" />
<i:Icon Value="fa-regular fa-address-card" Grid.Row="3" />
<Button i:Attached.Icon="fa-regular fa-address-card" Grid.Row="3" Classes="disableByStyle" />

<!--info-->
<TextBlock Text="fa-solid fa(-circle)-info" Grid.Row="4" />
<i:Icon Value="fa-solid fa-info" Grid.Row="4" Foreground="DodgerBlue" />
<Image Classes="icon" Grid.Row="4">
<Image.Source>
<i:IconImage Brush="DodgerBlue" Value="fa-solid fa-info" />
</Image.Source>
</Image>
<Button i:Attached.Icon="fa-solid fa-circle-info" Grid.Row="4" Foreground="DodgerBlue" Classes="disableByStyle" />
</Grid>

<!--Material Design-->
<Grid Grid.Column="1" RowDefinitions="Auto, Auto, Auto, Auto, Auto" ColumnDefinitions="Auto, Auto, Auto" Margin="32,4,4,4">
<TextBlock Text="Material Design" Grid.Row="0" Grid.ColumnSpan="2" Classes="h1" />
<!--GitHub-->

<!--github-->
<TextBlock Text="mdi-github" Grid.Row="1" />
<i:Icon Value="mdi-github" Grid.Row="1" Foreground="BlueViolet" />
<Button i:Attached.Icon="mdi-github" Grid.Row="1" Foreground="BlueViolet" IsEnabled="{Binding IsEnabled}" />

<!--circle-medium-->
<TextBlock Text="mdi-circle-medium" Grid.Row="2" />
<i:Icon Value="mdi-circle-medium" Grid.Row="2" />
<Button i:Attached.Icon="mdi-circle-medium" Grid.Row="2" IsEnabled="{Binding IsEnabled}" />
<Button i:Attached.Icon="mdi-circle-medium" Grid.Row="2" IsEnabled="{Binding IsEnabled}" />

<!--map-marker-outline-->
<TextBlock Text="mdi-map-marker-outline" Grid.Row="3" />
<i:Icon Value="mdi-map-marker-outline" Grid.Row="3" />
<Button i:Attached.Icon="mdi-map-marker-outline" Grid.Row="3" IsEnabled="{Binding IsEnabled}" />

<!--information-->
<TextBlock Text="mdi-information(-variant)" Grid.Row="4" />
<i:Icon Value="mdi-information-variant" Grid.Row="4" Foreground="DodgerBlue" />
<Image Classes="icon" Grid.Row="4">
<Image.Source>
<i:IconImage Brush="DodgerBlue" Value="mdi-information-variant" />
</Image.Source>
</Image>
<Button i:Attached.Icon="mdi-information" Grid.Row="4" Foreground="DodgerBlue" IsEnabled="{Binding IsEnabled}" />
</Grid>

Expand Down
Binary file modified resources/demo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 6 additions & 11 deletions src/Projektanker.Icons.Avalonia/Icon.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,12 @@
</TemplatedControl.RenderTransform>
<TemplatedControl.Template>
<ControlTemplate>
<Viewbox Width="{TemplateBinding FontSize}"
Height="{TemplateBinding FontSize}">
<Canvas Width="{Binding Canvas.Width, RelativeSource={RelativeSource TemplatedParent}}"
Height="{Binding Canvas.Height, RelativeSource={RelativeSource TemplatedParent}}"
ClipToBounds="True">
<Path Canvas.Left="{Binding Path.Left, RelativeSource={RelativeSource TemplatedParent}}"
Canvas.Top="{Binding Path.Right, RelativeSource={RelativeSource TemplatedParent}}"
Data="{Binding Path.Data, RelativeSource={RelativeSource TemplatedParent}}"
Fill="{TemplateBinding Foreground}" />
</Canvas>
</Viewbox>
<Image Width="{TemplateBinding FontSize}"
Height="{TemplateBinding FontSize}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="Uniform"
Source="{TemplateBinding Image}" />
</ControlTemplate>
</TemplatedControl.Template>
</TemplatedControl>
53 changes: 16 additions & 37 deletions src/Projektanker.Icons.Avalonia/Icon.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using Avalonia;
using Avalonia.Controls.Primitives;
using Avalonia.Media;
using Projektanker.Icons.Avalonia.ViewModels;

#nullable enable

namespace Projektanker.Icons.Avalonia
{
Expand All @@ -13,20 +14,10 @@ public class Icon : TemplatedControl
public static readonly StyledProperty<IconAnimation> AnimationProperty =
AvaloniaProperty.Register<Icon, IconAnimation>(nameof(Animation));

internal static readonly DirectProperty<Icon, CanvasViewModel> CanvasProperty =
AvaloniaProperty.RegisterDirect<Icon, CanvasViewModel>(nameof(Canvas), o => o.Canvas);

internal static readonly DirectProperty<Icon, PathViewModel> PathProperty =
AvaloniaProperty.RegisterDirect<Icon, PathViewModel>(nameof(Path), o => o.Path);

private CanvasViewModel _canvas;
private PathViewModel _path;
internal static readonly DirectProperty<Icon, IImage?> ImageProperty =
AvaloniaProperty.RegisterDirect<Icon, IImage?>(nameof(Image), o => o.Image);

static Icon()
{
AffectsRender<Icon>(PathProperty, CanvasProperty);
ValueProperty.Changed.Subscribe(e => HandleValueChanged(e));
}
private IImage? _image;

public string Value
{
Expand All @@ -40,36 +31,24 @@ public IconAnimation Animation
set => SetValue(AnimationProperty, value);
}

internal CanvasViewModel Canvas
{
get => _canvas;
private set => SetAndRaise(CanvasProperty, ref _canvas, value);
}

internal PathViewModel Path
internal IImage? Image
{
get => _path;
private set => SetAndRaise(PathProperty, ref _path, value);
get => _image;
private set => SetAndRaise(ImageProperty, ref _image, value);
}

private static void HandleValueChanged(AvaloniaPropertyChangedEventArgs<string> args)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
if (args.Sender is not Icon icon)
base.OnPropertyChanged(change);
if (change.Property == ValueProperty || change.Property == ForegroundProperty)
{
return;
UpdateIconImage();
}
}

var value = args.NewValue.GetValueOrDefault(string.Empty);
var iconModel = IconProvider.Current.GetIcon(value);

icon.Canvas = new CanvasViewModel(
width: iconModel.ViewBox.Width,
height: iconModel.ViewBox.Height);

icon.Path = new PathViewModel(
left: iconModel.ViewBox.X * -1,
right: iconModel.ViewBox.Y * -1,
data: StreamGeometry.Parse(iconModel.Path));
private void UpdateIconImage()
{
Image = new IconImage(Value, Foreground);
}
}
}
130 changes: 130 additions & 0 deletions src/Projektanker.Icons.Avalonia/IconImage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
using Avalonia;
using Avalonia.Media;

#nullable enable

namespace Projektanker.Icons.Avalonia
{
public class IconImage : AvaloniaObject, IImage
{
public static readonly StyledProperty<string?> ValueProperty =
AvaloniaProperty.Register<IconImage, string?>(nameof(Value));

public static readonly StyledProperty<IBrush?> BrushProperty =
AvaloniaProperty.Register<IconImage, IBrush?>(nameof(Brush));

public static readonly StyledProperty<IPen?> PenProperty =
AvaloniaProperty.Register<IconImage, IPen?>(nameof(Pen));

private Rect _bounds;
private GeometryDrawing? _drawing;

public IconImage() : this(string.Empty, new SolidColorBrush(Colors.Black))
{
}

public IconImage(string? value, IBrush? brush)
{
Value = value;
Brush = brush;
HandleValueChanged();
HandleBrushChanged();
}

public string? Value
{
get => GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}

public IBrush? Brush
{
get => GetValue(BrushProperty);
set => SetValue(BrushProperty, value);
}

public IPen? Pen
{
get => GetValue(PenProperty);
set => SetValue(PenProperty, value);
}

/// <inheritdoc>
public Size Size => _bounds.Size;

/// <inheritdoc/>
void IImage.Draw(
DrawingContext context,
Rect sourceRect,
Rect destRect)
{
var drawing = _drawing;
if (drawing == null)
{
return;
}

var bounds = _bounds;
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);
}
}

protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == ValueProperty)
{
HandleValueChanged();
}
else if (change.Property == BrushProperty)
{
HandleBrushChanged();
}
else if (change.Property == PenProperty)
{
HandlePenChanged();
}
}

private void HandleValueChanged()
{
var iconModel = IconProvider.Current.GetIcon(Value);

_bounds = new Rect(
x: iconModel.ViewBox.X,
y: iconModel.ViewBox.Y,
width: iconModel.ViewBox.Width,
height: iconModel.ViewBox.Height);

var drawing = GetGeometryDrawing();
drawing.Geometry = StreamGeometry.Parse(iconModel.Path);
}

private void HandleBrushChanged()
{
var drawing = GetGeometryDrawing();
drawing.Brush = Brush;
}

private void HandlePenChanged()
{
var drawing = GetGeometryDrawing();
drawing.Pen = Pen;
}

private GeometryDrawing GetGeometryDrawing()
{
return _drawing ??= new GeometryDrawing();
}
}
}
Loading

0 comments on commit ddd7bb4

Please sign in to comment.