LANCommander/LANCommander.Launcher/Views/Components/HeroCard.axaml
2026-07-04 00:14:33 -05:00

93 lines
4.6 KiB
XML

<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vmComponents="using:LANCommander.Launcher.ViewModels.Components"
xmlns:controls="using:LANCommander.Launcher.Controls"
xmlns:local="using:LANCommander.Launcher.Views.Components"
x:Class="LANCommander.Launcher.Views.Components.HeroCard"
x:DataType="vmComponents:GameItemViewModel"
ClipToBounds="False">
<UserControl.Styles>
<Style Selector="Button.HeroCard">
<Setter Property="Padding" Value="0" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="CornerRadius" Value="4" />
<Setter Property="ClipToBounds" Value="True" />
<Setter Property="RenderTransform" Value="scale(1)" />
<Setter Property="Transitions">
<Setter.Value>
<Transitions>
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.18" Easing="CubicEaseOut" />
</Transitions>
</Setter.Value>
</Setter>
</Style>
<Style Selector="Button.hero-card:pointerover">
<Setter Property="RenderTransform" Value="scale(1.03)" />
</Style>
</UserControl.Styles>
<Button Classes="HeroCard"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Command="{Binding $parent[local:HeroCard].Command}"
CommandParameter="{Binding $parent[local:HeroCard].CommandParameter}">
<Panel>
<!-- Background: hero image -->
<Image controls:AsyncImage.Source="{Binding HeroPath}"
controls:AsyncImage.DecodeWidth="480"
Stretch="UniformToFill"
IsVisible="{Binding HeroPath, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
<!-- Fallback background when no hero: approximate a blur by decoding the
cover tiny (32px) and letting the GPU upscale it smoothly. This is a
static texture, unlike a live BlurEffect which re-renders every scroll
frame and was the main source of depot scroll jank. -->
<Panel IsVisible="{Binding HeroPath, Converter={x:Static StringConverters.IsNullOrEmpty}}">
<Image controls:AsyncImage.Source="{Binding CoverPath}"
controls:AsyncImage.DecodeWidth="32"
RenderOptions.BitmapInterpolationMode="HighQuality"
Stretch="UniformToFill" />
<Border Background="#55000000" />
</Panel>
<!-- Bottom gradient -->
<Border VerticalAlignment="Bottom" Height="100" IsHitTestVisible="False">
<Border.Background>
<LinearGradientBrush StartPoint="0%,0%" EndPoint="0%,100%">
<GradientStop Color="#00000000" Offset="0" />
<GradientStop Color="#EE000000" Offset="1" />
</LinearGradientBrush>
</Border.Background>
</Border>
<!-- Logo (bottom-left) -->
<Image controls:AsyncImage.Source="{Binding LogoPath}"
HorizontalAlignment="Left" VerticalAlignment="Bottom"
MaxWidth="190" MaxHeight="72"
Margin="14,0,0,14"
Stretch="Uniform"
IsVisible="{Binding LogoPath, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
<!-- Title fallback when no logo -->
<TextBlock Text="{Binding Title}"
HorizontalAlignment="Left" VerticalAlignment="Bottom"
Margin="14,0,14,14"
FontSize="15" FontWeight="Bold" Foreground="White"
TextTrimming="CharacterEllipsis"
IsVisible="{Binding LogoPath, Converter={x:Static StringConverters.IsNullOrEmpty}}" />
<!-- In-library badge -->
<Border Background="#CC000000" CornerRadius="4"
HorizontalAlignment="Right" VerticalAlignment="Top"
Height="16" Width="16"
Margin="6" IsVisible="{Binding ShowInLibraryBadge}">
<Icon Type="Regular" Value="Check" Width="9" Height="9" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Border>
</Panel>
</Button>
</UserControl>