Browse Source

Add HyperlinkButton (#14509)

* Add initial HyperlinkButton control

* Remove IsVisitedOnClickProperty

* Update HyperlinkButton control theme

* Add work-around for HyperlinkButton text underlines

* Enable Launcher in HyperlinkButton

* Add simple control theme for HyperlinkButton

* Fix HyperlinkButton :visited selector

* Correct set TextDecorations attached property

* Improve HyperlinkButton control themes

* Fix reversed accent colors
pull/14564/head
robloo 3 years ago
committed by GitHub
parent
commit
ab71f3852e
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 32
      samples/ControlCatalog/Pages/ButtonsPage.xaml
  2. 95
      src/Avalonia.Controls/HyperlinkButton.cs
  3. 38
      src/Avalonia.Themes.Fluent/Accents/FluentControlResources.xaml
  4. 2
      src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml
  5. 88
      src/Avalonia.Themes.Fluent/Controls/HyperlinkButton.xaml
  6. 5
      src/Avalonia.Themes.Simple/Accents/Base.xaml
  7. 51
      src/Avalonia.Themes.Simple/Controls/HyperlinkButton.xaml
  8. 2
      src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml

32
samples/ControlCatalog/Pages/ButtonsPage.xaml

@ -148,6 +148,38 @@
</StackPanel>
</Border>
<!-- HyperlinkButton -->
<Border Classes="header-border">
<StackPanel Orientation="Vertical"
Spacing="4">
<TextBlock Text="HyperlinkButton"
Classes="header" />
<TextBlock TextWrapping="Wrap">A button control that functions as a navigateable hyperlink.</TextBlock>
</StackPanel>
</Border>
<Border Classes="thin"
Padding="15">
<StackPanel Orientation="Vertical"
Spacing="8">
<StackPanel Orientation="Horizontal">
<HyperlinkButton x:Name="EnabledHyperlinkButton"
VerticalAlignment="Center"
NavigateUri="https://docs.avaloniaui.net/docs/welcome">
<TextBlock Text="Avalonia Docs" />
</HyperlinkButton>
<CheckBox IsChecked="{Binding #EnabledHyperlinkButton.IsVisited}"
Content="IsVisited"
VerticalAlignment="Center"
Margin="10,0,0,0" />
</StackPanel>
<HyperlinkButton IsEnabled="False"
NavigateUri="https://docs.avaloniaui.net/docs/welcome">
<TextBlock Text="Avalonia Docs" />
</HyperlinkButton>
</StackPanel>
</Border>
<!-- DropDownButton -->
<Border Classes="header-border">
<StackPanel Orientation="Vertical"

95
src/Avalonia.Controls/HyperlinkButton.cs

@ -0,0 +1,95 @@
using System;
using Avalonia.Controls.Metadata;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
namespace Avalonia.Controls
{
/// <summary>
/// A button control that functions as a navigateable hyperlink.
/// </summary>
[PseudoClasses(pcVisited)]
public class HyperlinkButton : Button
{
// See: https://www.w3schools.com/cssref/sel_visited.php
private const string pcVisited = ":visited";
/// <summary>
/// Defines the <see cref="IsVisited"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsVisitedProperty =
AvaloniaProperty.Register<HyperlinkButton, bool>(
nameof(IsVisited),
defaultValue: false);
/// <summary>
/// Defines the <see cref="NavigateUri"/> property.
/// </summary>
public static readonly StyledProperty<Uri?> NavigateUriProperty =
AvaloniaProperty.Register<HyperlinkButton, Uri?>(
nameof(NavigateUri),
defaultValue: null);
/// <summary>
/// Initializes a new instance of the <see cref="HyperlinkButton"/> class.
/// </summary>
public HyperlinkButton()
{
}
/// <summary>
/// Gets or sets a value indicating whether the <see cref="NavigateUri"/> has been visited.
/// </summary>
public bool IsVisited
{
get => GetValue(IsVisitedProperty);
set => SetValue(IsVisitedProperty, value);
}
/// <summary>
/// Gets or sets the Uniform Resource Identifier (URI) automatically navigated to when the
/// <see cref="HyperlinkButton"/> is clicked.
/// </summary>
/// <remarks>
/// The URI may be any website or file location that can be launched using the <see cref="ILauncher"/> service.
/// <br/><br/>
/// If a URI should not be automatically launched, leave this property unset and use the
/// <see cref="Button.Click"/> and <see cref="IsVisited"/> members directly.
/// </remarks>
public Uri? NavigateUri
{
get => GetValue(NavigateUriProperty);
set => SetValue(NavigateUriProperty, value);
}
/// <inheritdoc/>
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == HyperlinkButton.IsVisitedProperty)
{
PseudoClasses.Set(pcVisited, change.GetNewValue<bool>());
}
}
/// <inheritdoc/>
protected override void OnClick()
{
base.OnClick();
Uri? uri = NavigateUri;
if (uri is not null)
{
Dispatcher.UIThread.Post(async () =>
{
bool success = await TopLevel.GetTopLevel(this)!.Launcher.LaunchUriAsync(uri);
if (success)
{
SetCurrentValue(IsVisitedProperty, true);
}
});
}
}
}
}

38
src/Avalonia.Themes.Fluent/Accents/FluentControlResources.xaml

@ -102,6 +102,25 @@
<StaticResource x:Key="ToggleButtonBorderBrushIndeterminateDisabled"
ResourceKey="SystemControlDisabledTransparentBrush" />
<!-- Resources for HyperlinkButton.xaml -->
<Color x:Key="HyperlinkVisitedColor">#FF681DA8</Color>
<SolidColorBrush x:Key="HyperlinkVisitedBrush" Color="{StaticResource HyperlinkVisitedColor}" />
<SolidColorBrush x:Key="HyperlinkButtonForeground" Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="HyperlinkButtonForegroundPointerOver" Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="HyperlinkButtonForegroundPressed" Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="HyperlinkButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<SolidColorBrush x:Key="HyperlinkButtonForegroundVisited" Color="{StaticResource HyperlinkVisitedColor}" />
<SolidColorBrush x:Key="HyperlinkButtonForegroundVisitedPointerOver" Color="{StaticResource HyperlinkVisitedColor}" />
<SolidColorBrush x:Key="HyperlinkButtonForegroundVisitedPressed" Color="{StaticResource HyperlinkVisitedColor}" Opacity="0.9" />
<StaticResource x:Key="HyperlinkButtonBackground" ResourceKey="SystemControlTransparentBrush" />
<SolidColorBrush x:Key="HyperlinkButtonBackgroundPointerOver" Color="#09000000" />
<SolidColorBrush x:Key="HyperlinkButtonBackgroundPressed" Color="#06000000" />
<StaticResource x:Key="HyperlinkButtonBackgroundDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="HyperlinkButtonBorderBrush" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="HyperlinkButtonBorderBrushPointerOver" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="HyperlinkButtonBorderBrushPressed" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="HyperlinkButtonBorderBrushDisabled" ResourceKey="SystemControlTransparentBrush" />
<!-- Resources for ComboBox.xaml -->
<x:Double x:Key="ComboBoxThemeMinWidth">64</x:Double>
<Thickness x:Key="ComboBoxBorderThemeThickness">1</Thickness>
@ -897,6 +916,25 @@
<StaticResource x:Key="ToggleButtonBorderBrushIndeterminateDisabled"
ResourceKey="SystemControlDisabledTransparentBrush" />
<!-- Resources for HyperlinkButton.xaml -->
<Color x:Key="HyperlinkVisitedColor">#FFC58AF9</Color>
<SolidColorBrush x:Key="HyperlinkVisitedBrush" Color="{StaticResource HyperlinkVisitedColor}" />
<SolidColorBrush x:Key="HyperlinkButtonForeground" Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="HyperlinkButtonForegroundPointerOver" Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="HyperlinkButtonForegroundPressed" Color="{DynamicResource SystemAccentColorLight1}" />
<StaticResource x:Key="HyperlinkButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<SolidColorBrush x:Key="HyperlinkButtonForegroundVisited" Color="{StaticResource HyperlinkVisitedColor}" />
<SolidColorBrush x:Key="HyperlinkButtonForegroundVisitedPointerOver" Color="{StaticResource HyperlinkVisitedColor}" />
<SolidColorBrush x:Key="HyperlinkButtonForegroundVisitedPressed" Color="{StaticResource HyperlinkVisitedColor}" Opacity="0.9" />
<StaticResource x:Key="HyperlinkButtonBackground" ResourceKey="SystemControlTransparentBrush" />
<SolidColorBrush x:Key="HyperlinkButtonBackgroundPointerOver" Color="#0FFFFFFF" />
<SolidColorBrush x:Key="HyperlinkButtonBackgroundPressed" Color="#0AFFFFFF" />
<StaticResource x:Key="HyperlinkButtonBackgroundDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="HyperlinkButtonBorderBrush" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="HyperlinkButtonBorderBrushPointerOver" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="HyperlinkButtonBorderBrushPressed" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="HyperlinkButtonBorderBrushDisabled" ResourceKey="SystemControlTransparentBrush" />
<!-- Resources for ComboBox.xaml -->
<x:Double x:Key="ComboBoxThemeMinWidth">64</x:Double>
<Thickness x:Key="ComboBoxBorderThemeThickness">1</Thickness>

2
src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml

@ -2,6 +2,7 @@
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:ClassModifier="internal">
<Styles.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
@ -22,6 +23,7 @@
<MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/CheckBox.xaml" />
<MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/FlyoutPresenter.xaml" />
<MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/GridSplitter.xaml" />
<MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/HyperlinkButton.xaml" />
<MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ItemsControl.xaml" />
<MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Label.xaml" />
<MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ListBox.xaml" />

88
src/Avalonia.Themes.Fluent/Controls/HyperlinkButton.xaml

@ -0,0 +1,88 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:ClassModifier="internal">
<Design.PreviewWith>
<Border Padding="20">
<StackPanel Spacing="20">
<HyperlinkButton Content="Click Me!" />
</StackPanel>
</Border>
</Design.PreviewWith>
<Thickness x:Key="HyperlinkButtonBorderThemeThickness">1</Thickness>
<ControlTheme x:Key="{x:Type HyperlinkButton}" TargetType="HyperlinkButton">
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Background" Value="{DynamicResource HyperlinkButtonBackground}" />
<Setter Property="BackgroundSizing" Value="OuterBorderEdge" />
<Setter Property="Foreground" Value="{DynamicResource HyperlinkButtonForeground}" />
<Setter Property="BorderBrush" Value="{DynamicResource HyperlinkButtonBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource HyperlinkButtonBorderThemeThickness}" />
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
<Setter Property="Padding" Value="{DynamicResource ButtonPadding}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="RenderTransform" Value="none" />
<Setter Property="TextBlock.TextDecorations" Value="Underline" />
<Setter Property="Transitions">
<Transitions>
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:.075" />
</Transitions>
</Setter>
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter x:Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BackgroundSizing="{TemplateBinding BackgroundSizing}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Padding="{TemplateBinding Padding}"
RecognizesAccessKey="True"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
</ControlTemplate>
</Setter>
<Style Selector="^:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource HyperlinkButtonBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource HyperlinkButtonBorderBrushPointerOver}" />
<Setter Property="Foreground" Value="{DynamicResource HyperlinkButtonForegroundPointerOver}" />
</Style>
<Style Selector="^:pressed">
<Setter Property="RenderTransform" Value="scale(0.98)" />
</Style>
<Style Selector="^:pressed /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource HyperlinkButtonBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource HyperlinkButtonBorderBrushPressed}" />
<Setter Property="Foreground" Value="{DynamicResource HyperlinkButtonForegroundPressed}" />
</Style>
<Style Selector="^:disabled /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource HyperlinkButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource HyperlinkButtonBorderBrushDisabled}" />
<Setter Property="Foreground" Value="{DynamicResource HyperlinkButtonForegroundDisabled}" />
</Style>
<Style Selector="^:visited">
<Style Selector="^ /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource HyperlinkButtonForegroundVisited}" />
</Style>
<Style Selector="^:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource HyperlinkButtonForegroundVisitedPointerOver}" />
</Style>
<Style Selector="^:pressed /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource HyperlinkButtonForegroundVisitedPressed}" />
</Style>
</Style>
</ControlTheme>
</ResourceDictionary>

5
src/Avalonia.Themes.Simple/Accents/Base.xaml

@ -19,6 +19,7 @@
<Color x:Key="ThemeForegroundColor">#FF000000</Color>
<Color x:Key="HighlightColor">#FF086F9E</Color>
<Color x:Key="HighlightColor2">#FF096085</Color>
<Color x:Key="HyperlinkVisitedColor">#FF681DA8</Color>
<SolidColorBrush x:Key="ThemeBackgroundBrush" Color="{StaticResource ThemeBackgroundColor}" />
<SolidColorBrush x:Key="ThemeBorderLowBrush" Color="{StaticResource ThemeBorderLowColor}" />
@ -33,9 +34,9 @@
<SolidColorBrush x:Key="ThemeControlHighlightMidBrush" Color="{StaticResource ThemeControlHighlightMidColor}" />
<SolidColorBrush x:Key="ThemeControlHighlightHighBrush" Color="{StaticResource ThemeControlHighlightHighColor}" />
<SolidColorBrush x:Key="ThemeForegroundBrush" Color="{StaticResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="HighlightBrush" Color="{StaticResource HighlightColor}" />
<SolidColorBrush x:Key="HighlightBrush2" Color="{StaticResource HighlightColor2}" />
<SolidColorBrush x:Key="HyperlinkVisitedBrush" Color="{StaticResource HyperlinkVisitedColor}" />
<SolidColorBrush x:Key="RefreshVisualizerForeground" Color="Black" />
<SolidColorBrush x:Key="RefreshVisualizerBackground" Color="Transparent" />
@ -63,6 +64,7 @@
<Color x:Key="ThemeForegroundColor">#FFDEDEDE</Color>
<Color x:Key="HighlightColor">#FF119EDA</Color>
<Color x:Key="HighlightColor2">#FF096085</Color>
<Color x:Key="HyperlinkVisitedColor">#FFC58AF9</Color>
<SolidColorBrush x:Key="ThemeBackgroundBrush" Color="{StaticResource ThemeBackgroundColor}" />
<SolidColorBrush x:Key="ThemeBorderLowBrush" Color="{StaticResource ThemeBorderLowColor}" />
@ -79,6 +81,7 @@
<SolidColorBrush x:Key="ThemeForegroundBrush" Color="{StaticResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="HighlightBrush" Color="{StaticResource HighlightColor}" />
<SolidColorBrush x:Key="HighlightBrush2" Color="{StaticResource HighlightColor2}" />
<SolidColorBrush x:Key="HyperlinkVisitedBrush" Color="{StaticResource HyperlinkVisitedColor}" />
<SolidColorBrush x:Key="RefreshVisualizerForeground" Color="White" />
<SolidColorBrush x:Key="RefreshVisualizerBackground" Color="Transparent" />

51
src/Avalonia.Themes.Simple/Controls/HyperlinkButton.xaml

@ -0,0 +1,51 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:ClassModifier="internal">
<Design.PreviewWith>
<Border Padding="20">
<StackPanel Spacing="20">
<HyperlinkButton Content="Click Me!" />
</StackPanel>
</Border>
</Design.PreviewWith>
<ControlTheme x:Key="{x:Type HyperlinkButton}" TargetType="HyperlinkButton">
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BackgroundSizing" Value="OuterBorderEdge" />
<Setter Property="Foreground" Value="{DynamicResource ThemeAccentBrush}" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}" />
<Setter Property="Padding" Value="4" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="TextBlock.TextDecorations" Value="Underline" />
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter x:Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BackgroundSizing="{TemplateBinding BackgroundSizing}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Padding="{TemplateBinding Padding}"
RecognizesAccessKey="True"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
</ControlTemplate>
</Setter>
<Style Selector="^:disabled">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}" />
</Style>
<Style Selector="^:visited /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource HyperlinkVisitedBrush}" />
</Style>
</ControlTheme>
</ResourceDictionary>

2
src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml

@ -1,6 +1,7 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:ClassModifier="internal">
<Styles.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
@ -29,6 +30,7 @@
<MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/FlyoutPresenter.xaml" />
<MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/MenuFlyoutPresenter.xaml" />
<MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/GridSplitter.xaml" />
<MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/HyperlinkButton.xaml" />
<MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ItemsControl.xaml" />
<MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ListBoxItem.xaml" />
<MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ListBox.xaml" />

Loading…
Cancel
Save