mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-22 00:54:26 +08:00
0.7.8.1
凤凰网新闻组件优化、央广网新闻组件优化。
This commit is contained in:
@@ -21,7 +21,8 @@
|
|||||||
BorderBrush="Transparent"
|
BorderBrush="Transparent"
|
||||||
BorderThickness="0"
|
BorderThickness="0"
|
||||||
Padding="16,14,16,14">
|
Padding="16,14,16,14">
|
||||||
<Grid RowDefinitions="Auto,Auto,Auto,Auto"
|
<Grid x:Name="ContentGrid"
|
||||||
|
RowDefinitions="Auto,Auto,Auto,Auto"
|
||||||
RowSpacing="8">
|
RowSpacing="8">
|
||||||
<Grid Grid.Row="0"
|
<Grid Grid.Row="0"
|
||||||
ColumnDefinitions="*,Auto"
|
ColumnDefinitions="*,Auto"
|
||||||
|
|||||||
@@ -625,17 +625,18 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
|
|||||||
{
|
{
|
||||||
var scale = ResolveScale();
|
var scale = ResolveScale();
|
||||||
var totalWidth = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * BaseWidthCells;
|
var totalWidth = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * BaseWidthCells;
|
||||||
|
var totalHeight = Bounds.Height > 1 ? Bounds.Height : _currentCellSize * BaseHeightCells;
|
||||||
|
|
||||||
var unifiedMainRectangle = ResolveUnifiedMainRectangle();
|
var unifiedMainRectangle = ResolveUnifiedMainRectangle();
|
||||||
RootBorder.CornerRadius = unifiedMainRectangle;
|
RootBorder.CornerRadius = unifiedMainRectangle;
|
||||||
RootBorder.Padding = new Thickness(0);
|
RootBorder.Padding = new Thickness(0);
|
||||||
|
|
||||||
|
var horizontalPadding = Math.Clamp(16 * scale, 8, 24);
|
||||||
|
var verticalPadding = Math.Clamp(14 * scale, 7, 22);
|
||||||
CardBorder.CornerRadius = unifiedMainRectangle;
|
CardBorder.CornerRadius = unifiedMainRectangle;
|
||||||
CardBorder.Padding = new Thickness(
|
CardBorder.Padding = new Thickness(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding);
|
||||||
Math.Clamp(16 * scale, 8, 24),
|
|
||||||
Math.Clamp(14 * scale, 7, 22),
|
var innerWidth = Math.Max(100, totalWidth - horizontalPadding * 2);
|
||||||
Math.Clamp(16 * scale, 8, 24),
|
|
||||||
Math.Clamp(14 * scale, 7, 22));
|
|
||||||
|
|
||||||
var headlineFont = Math.Clamp(24 * scale, 12, 34);
|
var headlineFont = Math.Clamp(24 * scale, 12, 34);
|
||||||
BrandPrimaryTextBlock.FontSize = headlineFont;
|
BrandPrimaryTextBlock.FontSize = headlineFont;
|
||||||
@@ -649,7 +650,7 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
|
|||||||
RefreshGlyphIcon.FontSize = Math.Clamp(19 * scale, 11, 24);
|
RefreshGlyphIcon.FontSize = Math.Clamp(19 * scale, 11, 24);
|
||||||
RefreshLabelTextBlock.FontSize = Math.Clamp(22 * scale, 11, 29);
|
RefreshLabelTextBlock.FontSize = Math.Clamp(22 * scale, 11, 29);
|
||||||
|
|
||||||
var imageWidth = Math.Clamp(totalWidth * 0.20, 60, 170);
|
var imageWidth = Math.Clamp(innerWidth * 0.22, 60, 170);
|
||||||
var imageHeight = Math.Clamp(imageWidth * 0.56, 38, 94);
|
var imageHeight = Math.Clamp(imageWidth * 0.56, 38, 94);
|
||||||
News1ImageHost.Width = imageWidth;
|
News1ImageHost.Width = imageWidth;
|
||||||
News1ImageHost.Height = imageHeight;
|
News1ImageHost.Height = imageHeight;
|
||||||
@@ -657,6 +658,8 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
|
|||||||
News2ImageHost.Height = imageHeight;
|
News2ImageHost.Height = imageHeight;
|
||||||
News1ImageHost.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(16 * scale, 8, 22);
|
News1ImageHost.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(16 * scale, 8, 22);
|
||||||
News2ImageHost.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(16 * scale, 8, 22);
|
News2ImageHost.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(16 * scale, 8, 22);
|
||||||
|
News1ImageHost.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#3D4250") : Color.Parse("#E6E6E6"));
|
||||||
|
News2ImageHost.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#3D4250") : Color.Parse("#E6E6E6"));
|
||||||
|
|
||||||
var columnGap = Math.Clamp(12 * scale, 6, 18);
|
var columnGap = Math.Clamp(12 * scale, 6, 18);
|
||||||
NewsItem1Grid.ColumnSpacing = columnGap;
|
NewsItem1Grid.ColumnSpacing = columnGap;
|
||||||
@@ -664,25 +667,29 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
|
|||||||
NewsItem1Grid.ColumnDefinitions[1].Width = new GridLength(imageWidth);
|
NewsItem1Grid.ColumnDefinitions[1].Width = new GridLength(imageWidth);
|
||||||
NewsItem2Grid.ColumnDefinitions[1].Width = new GridLength(imageWidth);
|
NewsItem2Grid.ColumnDefinitions[1].Width = new GridLength(imageWidth);
|
||||||
|
|
||||||
var availableTextWidth = Math.Max(
|
var availableTextWidth = Math.Max(80, innerWidth - imageWidth - columnGap);
|
||||||
84,
|
|
||||||
totalWidth - imageWidth - columnGap - Math.Clamp(20 * scale, 10, 32));
|
|
||||||
News1TitleTextBlock.MaxWidth = availableTextWidth;
|
News1TitleTextBlock.MaxWidth = availableTextWidth;
|
||||||
News2TitleTextBlock.MaxWidth = availableTextWidth;
|
News2TitleTextBlock.MaxWidth = availableTextWidth;
|
||||||
|
|
||||||
var newsFont = Math.Clamp(21 * scale, 10.5, 28);
|
var newsFont = Math.Clamp(21 * scale, 10.5, 28);
|
||||||
News1TitleTextBlock.FontSize = newsFont;
|
News1TitleTextBlock.FontSize = newsFont;
|
||||||
News2TitleTextBlock.FontSize = newsFont;
|
News2TitleTextBlock.FontSize = newsFont;
|
||||||
var mainNewsLineHeight = newsFont * 1.14;
|
var mainNewsLineHeight = newsFont * 1.2;
|
||||||
News1TitleTextBlock.LineHeight = mainNewsLineHeight;
|
News1TitleTextBlock.LineHeight = mainNewsLineHeight;
|
||||||
News2TitleTextBlock.LineHeight = mainNewsLineHeight;
|
News2TitleTextBlock.LineHeight = mainNewsLineHeight;
|
||||||
var mainNewsMinHeight = mainNewsLineHeight * 2;
|
var mainNewsMinHeight = mainNewsLineHeight * 2.2;
|
||||||
News1TitleTextBlock.MinHeight = mainNewsMinHeight;
|
News1TitleTextBlock.MinHeight = mainNewsMinHeight;
|
||||||
News2TitleTextBlock.MinHeight = mainNewsMinHeight;
|
News2TitleTextBlock.MinHeight = mainNewsMinHeight;
|
||||||
StatusTextBlock.FontSize = Math.Clamp(16 * scale, 9, 24);
|
StatusTextBlock.FontSize = Math.Clamp(16 * scale, 9, 24);
|
||||||
News1TitleTextBlock.MaxLines = 2;
|
News1TitleTextBlock.MaxLines = 2;
|
||||||
News2TitleTextBlock.MaxLines = 2;
|
News2TitleTextBlock.MaxLines = 2;
|
||||||
|
|
||||||
|
var rowSpacing = Math.Clamp(8 * scale, 4, 14);
|
||||||
|
if (ContentGrid is Grid contentGrid && contentGrid.RowDefinitions.Count >= 4)
|
||||||
|
{
|
||||||
|
contentGrid.RowSpacing = rowSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var row in _extraNewsRows)
|
foreach (var row in _extraNewsRows)
|
||||||
{
|
{
|
||||||
row.RootGrid.ColumnSpacing = columnGap;
|
row.RootGrid.ColumnSpacing = columnGap;
|
||||||
@@ -694,11 +701,12 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
|
|||||||
row.ImageHost.Width = imageWidth;
|
row.ImageHost.Width = imageWidth;
|
||||||
row.ImageHost.Height = imageHeight;
|
row.ImageHost.Height = imageHeight;
|
||||||
row.ImageHost.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(16 * scale, 8, 22);
|
row.ImageHost.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(16 * scale, 8, 22);
|
||||||
|
row.ImageHost.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#3D4250") : Color.Parse("#E6E6E6"));
|
||||||
|
|
||||||
row.TitleTextBlock.MaxWidth = availableTextWidth;
|
row.TitleTextBlock.MaxWidth = availableTextWidth;
|
||||||
row.TitleTextBlock.FontSize = Math.Clamp(19 * scale, 10, 25);
|
row.TitleTextBlock.FontSize = Math.Clamp(19 * scale, 10, 25);
|
||||||
row.TitleTextBlock.LineHeight = row.TitleTextBlock.FontSize * 1.12;
|
row.TitleTextBlock.LineHeight = row.TitleTextBlock.FontSize * 1.2;
|
||||||
row.TitleTextBlock.MinHeight = row.TitleTextBlock.LineHeight * 2;
|
row.TitleTextBlock.MinHeight = row.TitleTextBlock.LineHeight * 2.2;
|
||||||
row.TitleTextBlock.MaxLines = 2;
|
row.TitleTextBlock.MaxLines = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,19 +22,36 @@
|
|||||||
BorderThickness="0"
|
BorderThickness="0"
|
||||||
Padding="14,14,14,14">
|
Padding="14,14,14,14">
|
||||||
<Grid x:Name="ContentGrid"
|
<Grid x:Name="ContentGrid"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto"
|
RowDefinitions="Auto,*">
|
||||||
RowSpacing="8">
|
|
||||||
<Grid x:Name="HeaderGrid"
|
<Grid x:Name="HeaderGrid"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
ColumnDefinitions="*,Auto"
|
ColumnDefinitions="Auto,*"
|
||||||
ColumnSpacing="10">
|
ColumnSpacing="10"
|
||||||
<TextBlock x:Name="BrandTextBlock"
|
Margin="0,0,0,8">
|
||||||
Text="凤凰网新闻"
|
<StackPanel Orientation="Horizontal"
|
||||||
Foreground="#E24B2D"
|
Spacing="0"
|
||||||
FontSize="28"
|
VerticalAlignment="Center">
|
||||||
FontWeight="Bold"
|
<TextBlock x:Name="BrandTextBlock"
|
||||||
VerticalAlignment="Center"
|
Text="鳳凰網"
|
||||||
TextTrimming="CharacterEllipsis" />
|
Foreground="#E24B2D"
|
||||||
|
FontSize="20"
|
||||||
|
FontWeight="Bold"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<Border x:Name="NewsBadge"
|
||||||
|
Background="#E24B2D"
|
||||||
|
CornerRadius="4"
|
||||||
|
Padding="6,2"
|
||||||
|
Margin="4,0,0,0"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<TextBlock x:Name="NewsBadgeText"
|
||||||
|
Text="新聞"
|
||||||
|
Foreground="White"
|
||||||
|
FontSize="20"
|
||||||
|
FontWeight="Bold"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<Button x:Name="RefreshButton"
|
<Button x:Name="RefreshButton"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
@@ -58,129 +75,18 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Border x:Name="NewsItem1Host"
|
<ScrollViewer x:Name="NewsScrollViewer"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Tag="0"
|
VerticalScrollBarVisibility="Auto">
|
||||||
Background="Transparent"
|
<StackPanel x:Name="NewsStackPanel" Spacing="6">
|
||||||
Padding="0,2"
|
<TextBlock x:Name="LoadingTextBlock"
|
||||||
PointerPressed="OnNewsItemPointerPressed">
|
Text="正在加载..."
|
||||||
<Grid x:Name="NewsItem1Grid"
|
Foreground="#6A6F77"
|
||||||
ColumnDefinitions="*,Auto"
|
FontSize="14"
|
||||||
ColumnSpacing="10">
|
HorizontalAlignment="Center"
|
||||||
<TextBlock x:Name="NewsItem1TextBlock"
|
IsVisible="False" />
|
||||||
Text="新闻标题"
|
</StackPanel>
|
||||||
Foreground="#202327"
|
</ScrollViewer>
|
||||||
FontSize="22"
|
|
||||||
FontWeight="SemiBold"
|
|
||||||
TextWrapping="Wrap"
|
|
||||||
TextTrimming="CharacterEllipsis"
|
|
||||||
MaxLines="2"
|
|
||||||
VerticalAlignment="Top" />
|
|
||||||
<Border x:Name="NewsItem1ImageHost"
|
|
||||||
Grid.Column="1"
|
|
||||||
Width="148"
|
|
||||||
Height="84"
|
|
||||||
CornerRadius="12"
|
|
||||||
ClipToBounds="True"
|
|
||||||
Background="#E6E8EC">
|
|
||||||
<Image x:Name="NewsItem1Image"
|
|
||||||
Stretch="UniformToFill" />
|
|
||||||
</Border>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<Border x:Name="NewsItem2Host"
|
|
||||||
Grid.Row="2"
|
|
||||||
Tag="1"
|
|
||||||
Background="Transparent"
|
|
||||||
Padding="0,2"
|
|
||||||
PointerPressed="OnNewsItemPointerPressed">
|
|
||||||
<Grid x:Name="NewsItem2Grid"
|
|
||||||
ColumnDefinitions="*,Auto"
|
|
||||||
ColumnSpacing="10">
|
|
||||||
<TextBlock x:Name="NewsItem2TextBlock"
|
|
||||||
Text="新闻标题"
|
|
||||||
Foreground="#202327"
|
|
||||||
FontSize="22"
|
|
||||||
FontWeight="SemiBold"
|
|
||||||
TextWrapping="Wrap"
|
|
||||||
TextTrimming="CharacterEllipsis"
|
|
||||||
MaxLines="2"
|
|
||||||
VerticalAlignment="Top" />
|
|
||||||
<Border x:Name="NewsItem2ImageHost"
|
|
||||||
Grid.Column="1"
|
|
||||||
Width="148"
|
|
||||||
Height="84"
|
|
||||||
CornerRadius="12"
|
|
||||||
ClipToBounds="True"
|
|
||||||
Background="#E6E8EC">
|
|
||||||
<Image x:Name="NewsItem2Image"
|
|
||||||
Stretch="UniformToFill" />
|
|
||||||
</Border>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<Border x:Name="NewsItem3Host"
|
|
||||||
Grid.Row="3"
|
|
||||||
Tag="2"
|
|
||||||
Background="Transparent"
|
|
||||||
Padding="0,2"
|
|
||||||
PointerPressed="OnNewsItemPointerPressed">
|
|
||||||
<Grid x:Name="NewsItem3Grid"
|
|
||||||
ColumnDefinitions="*,Auto"
|
|
||||||
ColumnSpacing="10">
|
|
||||||
<TextBlock x:Name="NewsItem3TextBlock"
|
|
||||||
Text="新闻标题"
|
|
||||||
Foreground="#202327"
|
|
||||||
FontSize="22"
|
|
||||||
FontWeight="SemiBold"
|
|
||||||
TextWrapping="Wrap"
|
|
||||||
TextTrimming="CharacterEllipsis"
|
|
||||||
MaxLines="2"
|
|
||||||
VerticalAlignment="Top" />
|
|
||||||
<Border x:Name="NewsItem3ImageHost"
|
|
||||||
Grid.Column="1"
|
|
||||||
Width="148"
|
|
||||||
Height="84"
|
|
||||||
CornerRadius="12"
|
|
||||||
ClipToBounds="True"
|
|
||||||
Background="#E6E8EC">
|
|
||||||
<Image x:Name="NewsItem3Image"
|
|
||||||
Stretch="UniformToFill" />
|
|
||||||
</Border>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<Border x:Name="NewsItem4Host"
|
|
||||||
Grid.Row="4"
|
|
||||||
Tag="3"
|
|
||||||
Background="Transparent"
|
|
||||||
Padding="0,2"
|
|
||||||
PointerPressed="OnNewsItemPointerPressed">
|
|
||||||
<Grid x:Name="NewsItem4Grid"
|
|
||||||
ColumnDefinitions="*,Auto"
|
|
||||||
ColumnSpacing="10">
|
|
||||||
<TextBlock x:Name="NewsItem4TextBlock"
|
|
||||||
Text="新闻标题"
|
|
||||||
Foreground="#202327"
|
|
||||||
FontSize="22"
|
|
||||||
FontWeight="SemiBold"
|
|
||||||
TextWrapping="Wrap"
|
|
||||||
TextTrimming="CharacterEllipsis"
|
|
||||||
MaxLines="2"
|
|
||||||
VerticalAlignment="Top" />
|
|
||||||
<Border x:Name="NewsItem4ImageHost"
|
|
||||||
Grid.Column="1"
|
|
||||||
Width="148"
|
|
||||||
Height="84"
|
|
||||||
CornerRadius="12"
|
|
||||||
ClipToBounds="True"
|
|
||||||
Background="#E6E8EC">
|
|
||||||
<Image x:Name="NewsItem4Image"
|
|
||||||
Stretch="UniformToFill" />
|
|
||||||
</Border>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
|
|||||||
private const double BaseCellSize = 48d;
|
private const double BaseCellSize = 48d;
|
||||||
private const int BaseWidthCells = 4;
|
private const int BaseWidthCells = 4;
|
||||||
private const int BaseHeightCells = 4;
|
private const int BaseHeightCells = 4;
|
||||||
private const int MaxDisplayItemCount = 4;
|
private const int MaxDisplayItemCount = 12;
|
||||||
private static readonly IReadOnlyList<int> SupportedAutoRefreshIntervalsMinutes = RefreshIntervalCatalog.SupportedIntervalsMinutes;
|
private static readonly IReadOnlyList<int> SupportedAutoRefreshIntervalsMinutes = RefreshIntervalCatalog.SupportedIntervalsMinutes;
|
||||||
|
|
||||||
private readonly DispatcherTimer _refreshTimer = new()
|
private readonly DispatcherTimer _refreshTimer = new()
|
||||||
@@ -47,9 +47,9 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
|
|||||||
private LanMountainDesktop.PluginSdk.ISettingsService _appSettingsService = LanMountainDesktop.Services.Settings.HostSettingsFacadeProvider.GetOrCreate().Settings;
|
private LanMountainDesktop.PluginSdk.ISettingsService _appSettingsService = LanMountainDesktop.Services.Settings.HostSettingsFacadeProvider.GetOrCreate().Settings;
|
||||||
private IComponentInstanceSettingsStore _componentSettingsService = HostComponentSettingsStoreProvider.GetOrCreate();
|
private IComponentInstanceSettingsStore _componentSettingsService = HostComponentSettingsStoreProvider.GetOrCreate();
|
||||||
private readonly LocalizationService _localizationService = new();
|
private readonly LocalizationService _localizationService = new();
|
||||||
private readonly List<DailyNewsItemSnapshot> _activeItems = [];
|
private readonly Dictionary<string, DailyNewsItemSnapshot> _newsByUrl = new(StringComparer.OrdinalIgnoreCase);
|
||||||
private readonly List<NewsItemVisual> _itemVisuals = [];
|
private readonly List<NewsItemControl> _itemControls = [];
|
||||||
private readonly Bitmap?[] _newsBitmaps = new Bitmap?[MaxDisplayItemCount];
|
private readonly Dictionary<string, Bitmap> _imageCache = new();
|
||||||
|
|
||||||
private IRecommendationInfoService _recommendationService = DefaultRecommendationService;
|
private IRecommendationInfoService _recommendationService = DefaultRecommendationService;
|
||||||
private CancellationTokenSource? _refreshCts;
|
private CancellationTokenSource? _refreshCts;
|
||||||
@@ -61,28 +61,13 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
|
|||||||
private bool _autoRefreshEnabled = true;
|
private bool _autoRefreshEnabled = true;
|
||||||
private bool _isNightVisual = true;
|
private bool _isNightVisual = true;
|
||||||
|
|
||||||
private sealed record NewsItemVisual(
|
|
||||||
Border Host,
|
|
||||||
Grid RowGrid,
|
|
||||||
TextBlock TitleTextBlock,
|
|
||||||
Border ImageHost,
|
|
||||||
Image ImageControl);
|
|
||||||
|
|
||||||
public IfengNewsWidget()
|
public IfengNewsWidget()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
BrandTextBlock.FontFamily = MiSansFontFamily;
|
BrandTextBlock.FontFamily = MiSansFontFamily;
|
||||||
NewsItem1TextBlock.FontFamily = MiSansFontFamily;
|
|
||||||
NewsItem2TextBlock.FontFamily = MiSansFontFamily;
|
|
||||||
NewsItem3TextBlock.FontFamily = MiSansFontFamily;
|
|
||||||
NewsItem4TextBlock.FontFamily = MiSansFontFamily;
|
|
||||||
StatusTextBlock.FontFamily = MiSansFontFamily;
|
StatusTextBlock.FontFamily = MiSansFontFamily;
|
||||||
|
LoadingTextBlock.FontFamily = MiSansFontFamily;
|
||||||
_itemVisuals.Add(new NewsItemVisual(NewsItem1Host, NewsItem1Grid, NewsItem1TextBlock, NewsItem1ImageHost, NewsItem1Image));
|
|
||||||
_itemVisuals.Add(new NewsItemVisual(NewsItem2Host, NewsItem2Grid, NewsItem2TextBlock, NewsItem2ImageHost, NewsItem2Image));
|
|
||||||
_itemVisuals.Add(new NewsItemVisual(NewsItem3Host, NewsItem3Grid, NewsItem3TextBlock, NewsItem3ImageHost, NewsItem3Image));
|
|
||||||
_itemVisuals.Add(new NewsItemVisual(NewsItem4Host, NewsItem4Grid, NewsItem4TextBlock, NewsItem4ImageHost, NewsItem4Image));
|
|
||||||
|
|
||||||
_refreshTimer.Tick += OnRefreshTimerTick;
|
_refreshTimer.Tick += OnRefreshTimerTick;
|
||||||
AttachedToVisualTree += OnAttachedToVisualTree;
|
AttachedToVisualTree += OnAttachedToVisualTree;
|
||||||
@@ -135,7 +120,7 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
|
|||||||
_isAttached = false;
|
_isAttached = false;
|
||||||
_refreshTimer.Stop();
|
_refreshTimer.Stop();
|
||||||
CancelRefreshRequest();
|
CancelRefreshRequest();
|
||||||
DisposeNewsBitmaps();
|
DisposeImageCache();
|
||||||
UpdateRefreshButtonState();
|
UpdateRefreshButtonState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,18 +176,19 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
|
|||||||
CardBorder.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#1B2129") : Color.Parse("#FCFCFD"));
|
CardBorder.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#1B2129") : Color.Parse("#FCFCFD"));
|
||||||
RootBorder.BorderBrush = new SolidColorBrush(_isNightVisual ? Color.Parse("#33FFFFFF") : Color.Parse("#00000000"));
|
RootBorder.BorderBrush = new SolidColorBrush(_isNightVisual ? Color.Parse("#33FFFFFF") : Color.Parse("#00000000"));
|
||||||
|
|
||||||
BrandTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#202327"));
|
BrandTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#FF6B5A") : Color.Parse("#E24B2D"));
|
||||||
|
NewsBadge.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#FF6B5A") : Color.Parse("#E24B2D"));
|
||||||
|
|
||||||
RefreshButton.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#2D3440") : Color.Parse("#EFF1F5"));
|
RefreshButton.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#2D3440") : Color.Parse("#EFF1F5"));
|
||||||
RefreshGlyphIcon.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#A8B1C2") : Color.Parse("#5E6671"));
|
RefreshGlyphIcon.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#A8B1C2") : Color.Parse("#5E6671"));
|
||||||
|
|
||||||
foreach (var visual in _itemVisuals)
|
|
||||||
{
|
|
||||||
visual.Host.Background = Brushes.Transparent;
|
|
||||||
visual.TitleTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#202327"));
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#8B95A5") : Color.Parse("#6A6F77"));
|
StatusTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#8B95A5") : Color.Parse("#6A6F77"));
|
||||||
|
LoadingTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#8B95A5") : Color.Parse("#6A6F77"));
|
||||||
|
|
||||||
|
foreach (var control in _itemControls)
|
||||||
|
{
|
||||||
|
control.ApplyNightMode(_isNightVisual);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnRefreshTimerTick(object? sender, EventArgs e)
|
private async void OnRefreshTimerTick(object? sender, EventArgs e)
|
||||||
@@ -217,22 +203,6 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
|
|||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnNewsItemPointerPressed(object? sender, PointerPressedEventArgs e)
|
|
||||||
{
|
|
||||||
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed ||
|
|
||||||
sender is not Border host ||
|
|
||||||
host.Tag is null ||
|
|
||||||
!int.TryParse(host.Tag.ToString(), out var index) ||
|
|
||||||
index < 0 ||
|
|
||||||
index >= _activeItems.Count)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TryOpenUrl(_activeItems[index].Url);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task RefreshNewsAsync(bool forceRefresh)
|
private async Task RefreshNewsAsync(bool forceRefresh)
|
||||||
{
|
{
|
||||||
if (!_isAttached || _isRefreshing)
|
if (!_isAttached || _isRefreshing)
|
||||||
@@ -272,7 +242,6 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
|
|||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
// Ignore canceled requests.
|
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -296,100 +265,90 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
|
|||||||
|
|
||||||
private async Task ApplySnapshotAsync(DailyNewsSnapshot snapshot, CancellationToken cancellationToken)
|
private async Task ApplySnapshotAsync(DailyNewsSnapshot snapshot, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
BrandTextBlock.Text = L("ifeng.widget.brand", "凤凰网新闻");
|
|
||||||
ToolTip.SetTip(RefreshButton, L("ifeng.widget.refresh_tooltip", "刷新"));
|
ToolTip.SetTip(RefreshButton, L("ifeng.widget.refresh_tooltip", "刷新"));
|
||||||
|
|
||||||
_activeItems.Clear();
|
var newItems = snapshot.Items
|
||||||
foreach (var item in snapshot.Items)
|
.Where(item => !string.IsNullOrWhiteSpace(item.Url) && !_newsByUrl.ContainsKey(item.Url))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (newItems.Count == 0 && _itemControls.Count == 0)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(item.Title) || string.IsNullOrWhiteSpace(item.Url))
|
ApplyEmptyState();
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_activeItems.Add(item);
|
|
||||||
if (_activeItems.Count >= MaxDisplayItemCount)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var fallbackText = L("ifeng.widget.fallback_item", "暂无新闻");
|
|
||||||
for (var i = 0; i < _itemVisuals.Count; i++)
|
|
||||||
{
|
|
||||||
var visual = _itemVisuals[i];
|
|
||||||
visual.Host.IsVisible = true;
|
|
||||||
visual.TitleTextBlock.Text = i < _activeItems.Count
|
|
||||||
? NormalizeCompactText(_activeItems[i].Title)
|
|
||||||
: fallbackText;
|
|
||||||
SetNewsBitmap(i, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusTextBlock.IsVisible = false;
|
|
||||||
UpdateInteractionState();
|
|
||||||
UpdateAdaptiveLayout();
|
|
||||||
|
|
||||||
var tasks = Enumerable.Range(0, MaxDisplayItemCount)
|
|
||||||
.Select(index => TryDownloadBitmapAsync(
|
|
||||||
index < _activeItems.Count ? _activeItems[index].ImageUrl : null,
|
|
||||||
cancellationToken))
|
|
||||||
.ToArray();
|
|
||||||
var bitmaps = await Task.WhenAll(tasks);
|
|
||||||
if (cancellationToken.IsCancellationRequested || !_isAttached)
|
|
||||||
{
|
|
||||||
foreach (var bitmap in bitmaps)
|
|
||||||
{
|
|
||||||
bitmap?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < bitmaps.Length; i++)
|
foreach (var item in newItems)
|
||||||
{
|
{
|
||||||
SetNewsBitmap(i, bitmaps[i]);
|
_newsByUrl[item.Url] = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
if (!_isAttached) return;
|
||||||
|
|
||||||
|
LoadingTextBlock.IsVisible = false;
|
||||||
|
StatusTextBlock.IsVisible = false;
|
||||||
|
|
||||||
|
foreach (var item in newItems)
|
||||||
|
{
|
||||||
|
var control = new NewsItemControl(item, _isNightVisual);
|
||||||
|
control.Clicked += (s, url) => TryOpenUrl(url);
|
||||||
|
NewsStackPanel.Children.Insert(NewsStackPanel.Children.Count - 1, control);
|
||||||
|
_itemControls.Add(control);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateAdaptiveLayout();
|
||||||
|
});
|
||||||
|
|
||||||
|
var imageTasks = newItems.Select(async item =>
|
||||||
|
{
|
||||||
|
var bitmap = await TryDownloadBitmapAsync(item.ImageUrl, cancellationToken);
|
||||||
|
if (bitmap != null && !cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
if (_imageCache.TryGetValue(item.Url, out var oldBitmap))
|
||||||
|
{
|
||||||
|
oldBitmap.Dispose();
|
||||||
|
}
|
||||||
|
_imageCache[item.Url] = bitmap;
|
||||||
|
|
||||||
|
var control = _itemControls.FirstOrDefault(c => c.NewsUrl == item.Url);
|
||||||
|
control?.SetImage(bitmap);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Task.WhenAll(imageTasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyLoadingState()
|
private void ApplyLoadingState()
|
||||||
{
|
{
|
||||||
BrandTextBlock.Text = L("ifeng.widget.brand", "凤凰网新闻");
|
|
||||||
ToolTip.SetTip(RefreshButton, L("ifeng.widget.refresh_tooltip", "刷新"));
|
ToolTip.SetTip(RefreshButton, L("ifeng.widget.refresh_tooltip", "刷新"));
|
||||||
|
|
||||||
_activeItems.Clear();
|
LoadingTextBlock.Text = L("ifeng.widget.loading", "加载中...");
|
||||||
var loadingText = L("ifeng.widget.loading_item", "加载中...");
|
LoadingTextBlock.IsVisible = true;
|
||||||
for (var i = 0; i < _itemVisuals.Count; i++)
|
StatusTextBlock.IsVisible = false;
|
||||||
{
|
|
||||||
var visual = _itemVisuals[i];
|
|
||||||
visual.Host.IsVisible = true;
|
|
||||||
visual.TitleTextBlock.Text = loadingText;
|
|
||||||
SetNewsBitmap(i, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusTextBlock.Text = L("ifeng.widget.loading", "加载中...");
|
|
||||||
StatusTextBlock.IsVisible = true;
|
|
||||||
UpdateInteractionState();
|
|
||||||
UpdateAdaptiveLayout();
|
UpdateAdaptiveLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyFailedState()
|
private void ApplyFailedState()
|
||||||
{
|
{
|
||||||
BrandTextBlock.Text = L("ifeng.widget.brand", "凤凰网新闻");
|
|
||||||
ToolTip.SetTip(RefreshButton, L("ifeng.widget.refresh_tooltip", "刷新"));
|
ToolTip.SetTip(RefreshButton, L("ifeng.widget.refresh_tooltip", "刷新"));
|
||||||
|
|
||||||
_activeItems.Clear();
|
LoadingTextBlock.IsVisible = false;
|
||||||
var fallbackText = L("ifeng.widget.fallback_item", "暂无新闻");
|
|
||||||
for (var i = 0; i < _itemVisuals.Count; i++)
|
|
||||||
{
|
|
||||||
var visual = _itemVisuals[i];
|
|
||||||
visual.Host.IsVisible = true;
|
|
||||||
visual.TitleTextBlock.Text = fallbackText;
|
|
||||||
SetNewsBitmap(i, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusTextBlock.Text = L("ifeng.widget.fetch_failed", "新闻获取失败");
|
StatusTextBlock.Text = L("ifeng.widget.fetch_failed", "新闻获取失败");
|
||||||
StatusTextBlock.IsVisible = true;
|
StatusTextBlock.IsVisible = true;
|
||||||
UpdateInteractionState();
|
UpdateAdaptiveLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyEmptyState()
|
||||||
|
{
|
||||||
|
ToolTip.SetTip(RefreshButton, L("ifeng.widget.refresh_tooltip", "刷新"));
|
||||||
|
|
||||||
|
LoadingTextBlock.IsVisible = false;
|
||||||
|
StatusTextBlock.Text = L("ifeng.widget.fallback_item", "暂无新闻");
|
||||||
|
StatusTextBlock.IsVisible = true;
|
||||||
UpdateAdaptiveLayout();
|
UpdateAdaptiveLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,26 +367,13 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
|
|||||||
var verticalPadding = Math.Clamp(14 * softScale, 8, 20);
|
var verticalPadding = Math.Clamp(14 * softScale, 8, 20);
|
||||||
CardBorder.Padding = new Thickness(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding);
|
CardBorder.Padding = new Thickness(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding);
|
||||||
|
|
||||||
var rowSpacing = Math.Clamp(8 * softScale, 4, 12);
|
var headerHeight = Math.Clamp(totalHeight * 0.10, 28, 54);
|
||||||
ContentGrid.RowSpacing = rowSpacing;
|
HeaderGrid.Height = headerHeight;
|
||||||
HeaderGrid.ColumnSpacing = Math.Clamp(10 * softScale, 6, 16);
|
HeaderGrid.Margin = new Thickness(0, 0, 0, Math.Clamp(8 * softScale, 4, 12));
|
||||||
|
|
||||||
var innerWidth = Math.Max(150, totalWidth - horizontalPadding * 2d);
|
var brandFontSize = Math.Clamp(headerHeight * 0.62, 14, 30);
|
||||||
var innerHeight = Math.Max(160, totalHeight - verticalPadding * 2d);
|
BrandTextBlock.FontSize = brandFontSize;
|
||||||
var availableRowsHeight = Math.Max(120, innerHeight - rowSpacing * 4d);
|
NewsBadgeText.FontSize = brandFontSize;
|
||||||
var headerHeight = Math.Clamp(availableRowsHeight * 0.16, 24, 54);
|
|
||||||
var itemHeight = Math.Max(32, (availableRowsHeight - headerHeight) / 4d);
|
|
||||||
|
|
||||||
if (ContentGrid.RowDefinitions.Count >= 5)
|
|
||||||
{
|
|
||||||
ContentGrid.RowDefinitions[0].Height = new GridLength(headerHeight);
|
|
||||||
for (var i = 1; i <= 4; i++)
|
|
||||||
{
|
|
||||||
ContentGrid.RowDefinitions[i].Height = new GridLength(itemHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BrandTextBlock.FontSize = Math.Clamp(headerHeight * 0.62, 14, 30);
|
|
||||||
|
|
||||||
var refreshSize = Math.Clamp(headerHeight * 0.84, 22, 44);
|
var refreshSize = Math.Clamp(headerHeight * 0.84, 22, 44);
|
||||||
RefreshButton.Width = refreshSize;
|
RefreshButton.Width = refreshSize;
|
||||||
@@ -435,51 +381,25 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
|
|||||||
RefreshButton.CornerRadius = new CornerRadius(refreshSize / 2d);
|
RefreshButton.CornerRadius = new CornerRadius(refreshSize / 2d);
|
||||||
RefreshGlyphIcon.FontSize = Math.Clamp(refreshSize * 0.44, 10, 20);
|
RefreshGlyphIcon.FontSize = Math.Clamp(refreshSize * 0.44, 10, 20);
|
||||||
|
|
||||||
|
var innerWidth = Math.Max(150, totalWidth - horizontalPadding * 2d);
|
||||||
var imageWidth = Math.Clamp(innerWidth * 0.27, 82, 176);
|
var imageWidth = Math.Clamp(innerWidth * 0.27, 82, 176);
|
||||||
var imageHeight = Math.Clamp(imageWidth * 0.56, 46, 98);
|
var imageHeight = Math.Clamp(imageWidth * 0.56, 46, 98);
|
||||||
var columnGap = Math.Clamp(itemHeight * 0.20, 6, 14);
|
|
||||||
var rowPadding = Math.Clamp(itemHeight * 0.08, 1, 5);
|
|
||||||
var textWidth = Math.Max(84, innerWidth - imageWidth - columnGap);
|
|
||||||
var titleFont = Math.Clamp(itemHeight * 0.32, 12, 24);
|
|
||||||
|
|
||||||
foreach (var visual in _itemVisuals)
|
var baseTitleFont = 14;
|
||||||
|
var areaFactor = (totalWidth * totalHeight) / (BaseWidthCells * BaseCellSize * BaseHeightCells * BaseCellSize);
|
||||||
|
var adaptiveTitleFont = baseTitleFont * Math.Sqrt(Math.Clamp(areaFactor, 0.6, 2.5));
|
||||||
|
var titleFont = Math.Clamp(adaptiveTitleFont, 11, 26);
|
||||||
|
|
||||||
|
foreach (var control in _itemControls)
|
||||||
{
|
{
|
||||||
visual.Host.Padding = new Thickness(0, rowPadding, 0, rowPadding);
|
control.UpdateLayout(softScale, innerWidth, imageWidth, imageHeight, titleFont);
|
||||||
visual.RowGrid.ColumnSpacing = columnGap;
|
|
||||||
if (visual.RowGrid.ColumnDefinitions.Count > 1)
|
|
||||||
{
|
|
||||||
visual.RowGrid.ColumnDefinitions[1].Width = new GridLength(imageWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
visual.ImageHost.Width = imageWidth;
|
|
||||||
visual.ImageHost.Height = imageHeight;
|
|
||||||
visual.ImageHost.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(imageHeight * 0.15, 8, 16);
|
|
||||||
|
|
||||||
visual.TitleTextBlock.MaxWidth = textWidth;
|
|
||||||
visual.TitleTextBlock.FontSize = titleFont;
|
|
||||||
visual.TitleTextBlock.LineHeight = titleFont * 1.12;
|
|
||||||
visual.TitleTextBlock.MinHeight = visual.TitleTextBlock.LineHeight * 2;
|
|
||||||
visual.TitleTextBlock.MaxLines = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusTextBlock.FontSize = Math.Clamp(titleFont, 10, 20);
|
StatusTextBlock.FontSize = Math.Clamp(titleFont, 10, 24);
|
||||||
|
LoadingTextBlock.FontSize = Math.Clamp(titleFont, 10, 24);
|
||||||
ApplyNightModeVisual();
|
ApplyNightModeVisual();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateInteractionState()
|
|
||||||
{
|
|
||||||
for (var i = 0; i < _itemVisuals.Count; i++)
|
|
||||||
{
|
|
||||||
var visual = _itemVisuals[i];
|
|
||||||
var enabled = i < _activeItems.Count && !string.IsNullOrWhiteSpace(_activeItems[i].Url);
|
|
||||||
visual.Host.IsHitTestVisible = enabled;
|
|
||||||
visual.Host.Opacity = enabled ? 1.0 : 0.68;
|
|
||||||
visual.Host.Cursor = enabled
|
|
||||||
? new Cursor(StandardCursorType.Hand)
|
|
||||||
: new Cursor(StandardCursorType.Arrow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateRefreshButtonState()
|
private void UpdateRefreshButtonState()
|
||||||
{
|
{
|
||||||
var enabled = _isAttached && !_isRefreshing;
|
var enabled = _isAttached && !_isRefreshing;
|
||||||
@@ -515,7 +435,6 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
// Keep fallback defaults.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_autoRefreshEnabled = enabled;
|
_autoRefreshEnabled = enabled;
|
||||||
@@ -614,7 +533,6 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
// Ignore malformed URLs or shell launch failures.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -640,32 +558,13 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
|
|||||||
return uri.ToString();
|
return uri.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetNewsBitmap(int index, Bitmap? bitmap)
|
private void DisposeImageCache()
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= _newsBitmaps.Length)
|
foreach (var bitmap in _imageCache.Values)
|
||||||
{
|
{
|
||||||
bitmap?.Dispose();
|
bitmap.Dispose();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var visual = _itemVisuals[index];
|
|
||||||
var oldBitmap = _newsBitmaps[index];
|
|
||||||
if (ReferenceEquals(visual.ImageControl.Source, oldBitmap))
|
|
||||||
{
|
|
||||||
visual.ImageControl.Source = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
oldBitmap?.Dispose();
|
|
||||||
_newsBitmaps[index] = bitmap;
|
|
||||||
visual.ImageControl.Source = bitmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DisposeNewsBitmaps()
|
|
||||||
{
|
|
||||||
for (var i = 0; i < _newsBitmaps.Length; i++)
|
|
||||||
{
|
|
||||||
SetNewsBitmap(i, null);
|
|
||||||
}
|
}
|
||||||
|
_imageCache.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private double ResolveScale()
|
private double ResolveScale()
|
||||||
@@ -715,4 +614,142 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
|
|||||||
cts.Cancel();
|
cts.Cancel();
|
||||||
cts.Dispose();
|
cts.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class NewsItemControl : Border
|
||||||
|
{
|
||||||
|
private readonly DailyNewsItemSnapshot _item;
|
||||||
|
private readonly Grid _grid;
|
||||||
|
private readonly TextBlock _titleTextBlock;
|
||||||
|
private readonly Border _imageHost;
|
||||||
|
private readonly Image _imageControl;
|
||||||
|
private bool _isNightVisual;
|
||||||
|
private Point _pointerPressedPosition;
|
||||||
|
private bool _isPointerPressed;
|
||||||
|
|
||||||
|
public string NewsUrl => _item.Url;
|
||||||
|
|
||||||
|
public NewsItemControl(DailyNewsItemSnapshot item, bool isNightVisual)
|
||||||
|
{
|
||||||
|
_item = item;
|
||||||
|
_isNightVisual = isNightVisual;
|
||||||
|
|
||||||
|
Padding = new Thickness(0, 4);
|
||||||
|
Background = Brushes.Transparent;
|
||||||
|
Cursor = new Cursor(StandardCursorType.Hand);
|
||||||
|
|
||||||
|
PointerPressed += OnPointerPressed;
|
||||||
|
PointerReleased += OnPointerReleased;
|
||||||
|
PointerCaptureLost += OnPointerCaptureLost;
|
||||||
|
|
||||||
|
_titleTextBlock = new TextBlock
|
||||||
|
{
|
||||||
|
Text = NormalizeCompactText(item.Title),
|
||||||
|
Foreground = new SolidColorBrush(isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#202327")),
|
||||||
|
FontFamily = MiSansFontFamily,
|
||||||
|
FontWeight = FontWeight.SemiBold,
|
||||||
|
TextWrapping = TextWrapping.Wrap,
|
||||||
|
TextTrimming = TextTrimming.CharacterEllipsis,
|
||||||
|
MaxLines = 2,
|
||||||
|
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Top
|
||||||
|
};
|
||||||
|
|
||||||
|
_imageControl = new Image
|
||||||
|
{
|
||||||
|
Stretch = Stretch.UniformToFill
|
||||||
|
};
|
||||||
|
|
||||||
|
_imageHost = new Border
|
||||||
|
{
|
||||||
|
Width = 148,
|
||||||
|
Height = 84,
|
||||||
|
CornerRadius = new CornerRadius(12),
|
||||||
|
ClipToBounds = true,
|
||||||
|
Background = new SolidColorBrush(isNightVisual ? Color.Parse("#3D4250") : Color.Parse("#E6E8EC")),
|
||||||
|
Child = _imageControl
|
||||||
|
};
|
||||||
|
|
||||||
|
_grid = new Grid
|
||||||
|
{
|
||||||
|
ColumnDefinitions = ColumnDefinitions.Parse("*,Auto"),
|
||||||
|
ColumnSpacing = 10
|
||||||
|
};
|
||||||
|
|
||||||
|
Grid.SetColumn(_imageHost, 1);
|
||||||
|
_grid.Children.Add(_titleTextBlock);
|
||||||
|
_grid.Children.Add(_imageHost);
|
||||||
|
|
||||||
|
Child = _grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
|
||||||
|
{
|
||||||
|
_isPointerPressed = true;
|
||||||
|
_pointerPressedPosition = e.GetPosition(this);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPointerReleased(object? sender, PointerReleasedEventArgs e)
|
||||||
|
{
|
||||||
|
if (!_isPointerPressed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isPointerPressed = false;
|
||||||
|
var releasePosition = e.GetPosition(this);
|
||||||
|
var distance = Math.Sqrt(
|
||||||
|
Math.Pow(releasePosition.X - _pointerPressedPosition.X, 2) +
|
||||||
|
Math.Pow(releasePosition.Y - _pointerPressedPosition.Y, 2));
|
||||||
|
|
||||||
|
if (distance < 5)
|
||||||
|
{
|
||||||
|
Clicked?.Invoke(this, _item.Url);
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPointerCaptureLost(object? sender, PointerCaptureLostEventArgs e)
|
||||||
|
{
|
||||||
|
_isPointerPressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyNightMode(bool isNightVisual)
|
||||||
|
{
|
||||||
|
_isNightVisual = isNightVisual;
|
||||||
|
_titleTextBlock.Foreground = new SolidColorBrush(isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#202327"));
|
||||||
|
_imageHost.Background = new SolidColorBrush(isNightVisual ? Color.Parse("#3D4250") : Color.Parse("#E6E8EC"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateLayout(double scale, double innerWidth, double imageWidth, double imageHeight, double titleFont)
|
||||||
|
{
|
||||||
|
var columnGap = Math.Clamp(imageHeight * 0.20, 6, 14);
|
||||||
|
_grid.ColumnSpacing = columnGap;
|
||||||
|
|
||||||
|
if (_grid.ColumnDefinitions.Count > 1)
|
||||||
|
{
|
||||||
|
_grid.ColumnDefinitions[1] = new ColumnDefinition(new GridLength(imageWidth));
|
||||||
|
}
|
||||||
|
|
||||||
|
_imageHost.Width = imageWidth;
|
||||||
|
_imageHost.Height = imageHeight;
|
||||||
|
_imageHost.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(imageHeight * 0.15, 8, 16);
|
||||||
|
|
||||||
|
var textWidth = Math.Max(84, innerWidth - imageWidth - columnGap);
|
||||||
|
_titleTextBlock.MaxWidth = textWidth;
|
||||||
|
_titleTextBlock.FontSize = titleFont;
|
||||||
|
_titleTextBlock.LineHeight = titleFont * 1.12;
|
||||||
|
_titleTextBlock.MinHeight = _titleTextBlock.LineHeight * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetImage(Bitmap bitmap)
|
||||||
|
{
|
||||||
|
_imageControl.Source = bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<string>? Clicked;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user