Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Needs API review] Feature/icons #5867

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion samples/ControlCatalog/Pages/MenuPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
</MenuItem>
<MenuItem Header="Menu Item with _Icon" InputGesture="Ctrl+Shift+B">
<MenuItem.Icon>
<Image Source="/Assets/github_icon.png"/>
<ImageIcon Source="avares://ControlCatalog/Assets/test_icon.ico" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Menu Item with _Checkbox">
Expand Down
97 changes: 97 additions & 0 deletions src/Avalonia.Controls/FontIcon.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using Avalonia.Controls.Templates;
using Avalonia.Media;

namespace Avalonia.Controls
{
/// <summary>
/// Represents an icon that uses a glyph from the specified font.
/// </summary>
public class FontIcon : IconElement
{
/// <summary>
/// Gets the identifier for the <see cref="Glyph"/> dependency property.
/// </summary>
public static readonly StyledProperty<string> GlyphProperty =
AvaloniaProperty.Register<FontIcon, string>(nameof(Glyph));

/// <summary>
/// Gets or sets the character code that identifies the icon glyph.
/// </summary>
public string Glyph
{
get => GetValue(GlyphProperty);
set => SetValue(GlyphProperty, value);
}
}

/// <summary>
/// Represents an icon source that uses a glyph from the specified font.
/// </summary>
public class FontIconSource : IconSource
{
/// <inheritdoc cref="FontIcon.GlyphProperty" />
public static readonly StyledProperty<string> GlyphProperty =
FontIcon.GlyphProperty.AddOwner<FontIconSource>();

/// <inheritdoc cref="TextBlock.FontFamilyProperty" />
public static readonly StyledProperty<FontFamily> FontFamilyProperty =
TextBlock.FontFamilyProperty.AddOwner<FontIconSource>();

/// <inheritdoc cref="TextBlock.FontSizeProperty" />
public static readonly StyledProperty<double> FontSizeProperty =
TextBlock.FontSizeProperty.AddOwner<FontIconSource>();

/// <inheritdoc cref="TextBlock.FontStyleProperty" />
public static readonly StyledProperty<FontStyle> FontStyleProperty =
TextBlock.FontStyleProperty.AddOwner<FontIconSource>();

/// <inheritdoc cref="TextBlock.FontWeightProperty" />
public static readonly StyledProperty<FontWeight> FontWeightProperty =
TextBlock.FontWeightProperty.AddOwner<FontIconSource>();

/// <inheritdoc cref="FontIcon.Glyph" />
public string Glyph
{
get => GetValue(GlyphProperty);
set => SetValue(GlyphProperty, value);
}

/// <inheritdoc cref="TextBlock.FontFamily" />
public FontFamily FontFamily
{
get => GetValue(FontFamilyProperty);
set => SetValue(FontFamilyProperty, value);
}

/// <inheritdoc cref="TextBlock.FontSize" />
public double FontSize
{
get => GetValue(FontSizeProperty);
set => SetValue(FontSizeProperty, value);
}

/// <inheritdoc cref="TextBlock.FontStyle" />
public FontStyle FontStyle
{
get => GetValue(FontStyleProperty);
set => SetValue(FontStyleProperty, value);
}

/// <inheritdoc cref="TextBlock.FontWeight" />
public FontWeight FontWeight
{
get => GetValue(FontWeightProperty);
set => SetValue(FontWeightProperty, value);
}

public override IDataTemplate IconElementTemplate { get; } = new FuncDataTemplate<FontIconSource>((source, _) => new FontIcon
{
[!ForegroundProperty] = source[!ForegroundProperty],
[!GlyphProperty] = source[!GlyphProperty],
[!FontFamilyProperty] = source[!FontFamilyProperty],
[!FontSizeProperty] = source[!FontSizeProperty],
[!FontStyleProperty] = source[!FontStyleProperty],
[!FontWeightProperty] = source[!FontWeightProperty]
});
}
}
3 changes: 3 additions & 0 deletions src/Avalonia.Controls/IconElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace Avalonia.Controls
{
/// <summary>
/// Represents the base class for an icon UI element.
/// </summary>
public abstract class IconElement : TemplatedControl
{

Expand Down
59 changes: 59 additions & 0 deletions src/Avalonia.Controls/IconSourceElement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#nullable enable
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Media;
using Avalonia.Metadata;

namespace Avalonia.Controls
{
/// <summary>
/// Represents an icon that uses an IconSource as its content.
/// </summary>
/// <remarks>
/// <see cref="Avalonia.Controls.IconSource"/> is similar to IconElement. However, because it is not a <see cref="Control"/>, it can be shared.
/// <see cref="IconSourceElement"/> provides a wrapper that lets you use an IconSource in places that require an IconElement.
/// </remarks>
public class IconSourceElement : IconElement
{
/// <summary>
/// Identifies the IconSource dependency property.
/// </summary>
public static readonly StyledProperty<IconSource?> IconSourceProperty =
AvaloniaProperty.Register<IconSourceElement, IconSource?>(nameof(IconSource));

/// <summary>
/// Gets or sets the IconSource used as the icon content.
/// </summary>
[Content]
public IconSource? IconSource
{
get => GetValue(IconSourceProperty);
set => SetValue(IconSourceProperty, value);
}
}

/// <summary>
/// Represents the base class for an icon source.
/// </summary>
/// <remarks>
/// <see cref="IconSource"/> is similar to IconElement. However, because it is not a <see cref="Control"/>, it can be shared.
/// </remarks>
public abstract class IconSource : AvaloniaObject
{
/// <inheritdoc cref="TemplatedControl.ForegroundProperty" />
public static StyledProperty<IBrush?> ForegroundProperty =
TemplatedControl.ForegroundProperty.AddOwner<IconSource>();

/// <inheritdoc cref="TemplatedControl.Foreground" />
public IBrush? Foreground
{
get => GetValue(ForegroundProperty);
set => SetValue(ForegroundProperty, value);
}

/// <summary>
/// Gets the data template used to display <see cref="IconElement"/>.
/// </summary>
public abstract IDataTemplate IconElementTemplate { get; }
}
}
45 changes: 45 additions & 0 deletions src/Avalonia.Controls/ImageIcon.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Avalonia.Controls.Templates;
using Avalonia.Media;

namespace Avalonia.Controls
{
/// <summary>
/// Represents an icon that uses a image source as its content.
/// </summary>
public class ImageIcon : IconElement
{
/// <inheritdoc cref="Image.SourceProperty" />
public static readonly StyledProperty<IImage> SourceProperty =
Image.SourceProperty.AddOwner<ImageIcon>();

/// <inheritdoc cref="Image.Source" />
public IImage Source
{
get => GetValue(SourceProperty);
set => SetValue(SourceProperty, value);
}
}

/// <summary>
/// Represents an icon source that uses a image source as its content.
/// </summary>
public class ImageIconSource : IconSource
{
/// <inheritdoc cref="Image.SourceProperty" />
public static readonly StyledProperty<IImage> SourceProperty =
Image.SourceProperty.AddOwner<ImageIcon>();

/// <inheritdoc cref="Image.Source" />
public IImage Source
{
get => GetValue(SourceProperty);
set => SetValue(SourceProperty, value);
}

public override IDataTemplate IconElementTemplate { get; } = new FuncDataTemplate<ImageIconSource>((source, _) => new ImageIcon
{
[!ForegroundProperty] = source[!ForegroundProperty],
[!SourceProperty] = source[!SourceProperty]
});
}
}
30 changes: 27 additions & 3 deletions src/Avalonia.Controls/PathIcon.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;
using Avalonia.Media;

namespace Avalonia.Controls
Expand All @@ -9,13 +11,35 @@ static PathIcon()
AffectsRender<PathIcon>(DataProperty);
}

/// <inheritdoc cref="Path.DataProperty" />
public static readonly StyledProperty<Geometry> DataProperty =
AvaloniaProperty.Register<PathIcon, Geometry>(nameof(Data));
Path.DataProperty.AddOwner<PathIcon>();

/// <inheritdoc cref="Path.Data" />
public Geometry Data
{
get { return GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
get => GetValue(DataProperty);
set => SetValue(DataProperty, value);
}
}

public class PathIconSource : IconSource
{
/// <inheritdoc cref="Path.DataProperty" />
public static readonly StyledProperty<Geometry> DataProperty =
Path.DataProperty.AddOwner<PathIcon>();

/// <inheritdoc cref="Path.Data" />
public Geometry Data
{
get => GetValue(DataProperty);
set => SetValue(DataProperty, value);
}

public override IDataTemplate IconElementTemplate { get; } = new FuncDataTemplate<PathIconSource>((source, _) => new PathIcon
{
[!ForegroundProperty] = source[!ForegroundProperty],
[!DataProperty] = source[!DataProperty]
});
}
}
3 changes: 3 additions & 0 deletions src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/ContextMenu.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/MenuItem.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/OverlayPopupHost.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/FontIcon.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/PathIcon.xaml" />
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/ImageIcon.xaml" />
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/IconSourceElement.xaml" />
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/PopupRoot.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/ProgressBar.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/RadioButton.xaml"/>
Expand Down
33 changes: 33 additions & 0 deletions src/Avalonia.Themes.Fluent/Controls/FontIcon.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.PreviewWith>
<StackPanel Spacing="10" Margin="10">
<FontIcon Glyph="★" />
<FontIcon Glyph="" FontFamily="Segoe MDL2 Assets" />
</StackPanel>
</Design.PreviewWith>
<Style Selector="FontIcon">
<Setter Property="Foreground" Value="{DynamicResource TextControlForeground}" />
<Setter Property="FontSize" Value="15" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Height" Value="{DynamicResource IconElementThemeHeight}" />
<Setter Property="Width" Value="{DynamicResource IconElementThemeWidth}" />
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}">
<TextBlock Height="{TemplateBinding Height}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextAlignment="Center"
Width="{TemplateBinding Width}"
Text="{TemplateBinding Glyph}"
Foreground="{TemplateBinding Foreground}"
FontSize="{TemplateBinding FontSize}"
FontFamily="{TemplateBinding FontFamily}"
FontStyle="{TemplateBinding FontStyle}"
FontWeight="{TemplateBinding FontWeight}" />
</Border>
</ControlTemplate>
</Setter>
</Style>
</Styles>
45 changes: 45 additions & 0 deletions src/Avalonia.Themes.Fluent/Controls/IconSourceElement.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.PreviewWith>
<StackPanel Margin="10" Spacing="10">
<IconSourceElement>
<IconSourceElement.IconSource>
<ImageIconSource Source="avares://ControlCatalog/Assets/test_icon.ico" />
</IconSourceElement.IconSource>
</IconSourceElement>
<IconSourceElement>
<IconSourceElement.IconSource>
<PathIconSource Data="M100,10 40,198 190,78 10,78 160,198"
Foreground="Red" />
</IconSourceElement.IconSource>
</IconSourceElement>
<IconSourceElement>
<IconSourceElement.IconSource>
<FontIconSource Glyph="A"
FontWeight="Bold"
Foreground="Blue" />
</IconSourceElement.IconSource>
</IconSourceElement>
</StackPanel>
</Design.PreviewWith>

<Style Selector="IconSourceElement">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Height" Value="{DynamicResource IconElementThemeHeight}" />
<Setter Property="Width" Value="{DynamicResource IconElementThemeWidth}" />
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Background="{TemplateBinding Background}"
Content="{TemplateBinding IconSource}"
ContentTemplate="{Binding Path=IconSource.IconElementTemplate, RelativeSource={RelativeSource TemplatedParent}}" />
</ControlTemplate>
</Setter>
</Style>
<Style Selector="IconSourceElement > :is(IconElement)">
<Setter Property="Width" Value="NaN" />
<Setter Property="Height" Value="NaN" />
</Style>
</Styles>
24 changes: 24 additions & 0 deletions src/Avalonia.Themes.Fluent/Controls/ImageIcon.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.PreviewWith>
<StackPanel>
<ImageIcon Source="avares://ControlCatalog/Assets/test_icon.ico" />
</StackPanel>
</Design.PreviewWith>

<Style Selector="ImageIcon">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Height" Value="{DynamicResource IconElementThemeHeight}" />
<Setter Property="Width" Value="{DynamicResource IconElementThemeWidth}" />
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}">
<Image Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Source="{TemplateBinding Source}" />
</Border>
</ControlTemplate>
</Setter>
</Style>
</Styles>
6 changes: 4 additions & 2 deletions src/Avalonia.Themes.Fluent/Controls/PathIcon.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
<Viewbox Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}">
<Path Fill="{TemplateBinding Foreground}"
Data="{TemplateBinding Data}"
Stretch="Uniform" />
Data="{TemplateBinding Data}" />
</Viewbox>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="PathIcon /template/ Path">
<Setter Property="Stretch" Value="Uniform" />
</Style>
</Styles>