mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-22 09:14:25 +08:00
Compare commits
2 Commits
1c3cc76f21
...
v0.8.1.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
021c7ff245 | ||
|
|
675096b6c4 |
@@ -400,6 +400,29 @@
|
|||||||
"settings.status_bar.text_capsule_position.right": "Right",
|
"settings.status_bar.text_capsule_position.right": "Right",
|
||||||
"settings.status_bar.text_capsule_content_label": "Text content (Markdown supported)",
|
"settings.status_bar.text_capsule_content_label": "Text content (Markdown supported)",
|
||||||
"settings.status_bar.text_capsule_transparent_background_label": "Transparent background",
|
"settings.status_bar.text_capsule_transparent_background_label": "Transparent background",
|
||||||
|
"settings.status_bar.network_speed_header": "Network Speed",
|
||||||
|
"settings.status_bar.network_speed_description": "Display real-time network upload and download speed on the status bar.",
|
||||||
|
"settings.status_bar.network_speed_position_label": "Network speed position",
|
||||||
|
"settings.status_bar.network_speed_position.left": "Left",
|
||||||
|
"settings.status_bar.network_speed_position.center": "Center",
|
||||||
|
"settings.status_bar.network_speed_position.right": "Right",
|
||||||
|
"settings.status_bar.network_speed_mode_label": "Display mode",
|
||||||
|
"settings.status_bar.network_speed_mode.both": "Upload + Download",
|
||||||
|
"settings.status_bar.network_speed_mode.upload": "Upload only",
|
||||||
|
"settings.status_bar.network_speed_mode.download": "Download only",
|
||||||
|
"settings.status_bar.network_speed_transparent_background_label": "Transparent background",
|
||||||
|
"settings.status_bar.show_network_type_icon_label": "Show network type icon",
|
||||||
|
"settings.status_bar.shadow_header": "Status Bar Shadow",
|
||||||
|
"settings.status_bar.shadow_desc": "Add shadow effect to the status bar for better visibility of transparent components.",
|
||||||
|
"settings.status_bar.shadow_enabled_label": "Enable shadow",
|
||||||
|
"settings.status_bar.shadow_color_label": "Shadow color",
|
||||||
|
"settings.status_bar.shadow_opacity_label": "Shadow opacity",
|
||||||
|
"settings.status_bar.theme_header": "Status Bar Theme",
|
||||||
|
"settings.status_bar.theme_desc": "Set the theme mode for the status bar independently.",
|
||||||
|
"settings.status_bar.theme_mode_label": "Theme mode",
|
||||||
|
"settings.status_bar.theme_mode.follow_global": "Follow Global",
|
||||||
|
"settings.status_bar.theme_mode.dark": "Dark",
|
||||||
|
"settings.status_bar.theme_mode.light": "Light",
|
||||||
"settings.components.title": "Components",
|
"settings.components.title": "Components",
|
||||||
"settings.components.description": "Adjust component layout and corner design.",
|
"settings.components.description": "Adjust component layout and corner design.",
|
||||||
"settings.components.grid_header": "Grid Settings",
|
"settings.components.grid_header": "Grid Settings",
|
||||||
|
|||||||
@@ -343,6 +343,29 @@
|
|||||||
"settings.status_bar.text_capsule_position.right": "右",
|
"settings.status_bar.text_capsule_position.right": "右",
|
||||||
"settings.status_bar.text_capsule_content_label": "テキスト内容(Markdown対応)",
|
"settings.status_bar.text_capsule_content_label": "テキスト内容(Markdown対応)",
|
||||||
"settings.status_bar.text_capsule_transparent_background_label": "透明な背景",
|
"settings.status_bar.text_capsule_transparent_background_label": "透明な背景",
|
||||||
|
"settings.status_bar.network_speed_header": "ネットワーク速度",
|
||||||
|
"settings.status_bar.network_speed_description": "ステータスバーにリアルタイムのネットワーク速度を表示します。",
|
||||||
|
"settings.status_bar.network_speed_position_label": "ネットワーク速度の位置",
|
||||||
|
"settings.status_bar.network_speed_position.left": "左",
|
||||||
|
"settings.status_bar.network_speed_position.center": "中央",
|
||||||
|
"settings.status_bar.network_speed_position.right": "右",
|
||||||
|
"settings.status_bar.network_speed_mode_label": "表示モード",
|
||||||
|
"settings.status_bar.network_speed_mode.both": "アップロード + ダウンロード",
|
||||||
|
"settings.status_bar.network_speed_mode.upload": "アップロードのみ",
|
||||||
|
"settings.status_bar.network_speed_mode.download": "ダウンロードのみ",
|
||||||
|
"settings.status_bar.network_speed_transparent_background_label": "透明な背景",
|
||||||
|
"settings.status_bar.show_network_type_icon_label": "ネットワークタイプアイコンを表示",
|
||||||
|
"settings.status_bar.shadow_header": "ステータスバーの影",
|
||||||
|
"settings.status_bar.shadow_desc": "透明なコンポーネントの視認性を高めるために、ステータスバーに影効果を追加します。",
|
||||||
|
"settings.status_bar.shadow_enabled_label": "影を有効にする",
|
||||||
|
"settings.status_bar.shadow_color_label": "影の色",
|
||||||
|
"settings.status_bar.shadow_opacity_label": "影の不透明度",
|
||||||
|
"settings.status_bar.theme_header": "ステータスバーのテーマ",
|
||||||
|
"settings.status_bar.theme_desc": "ステータスバーのテーマモードを独立して設定します。",
|
||||||
|
"settings.status_bar.theme_mode_label": "テーマモード",
|
||||||
|
"settings.status_bar.theme_mode.follow_global": "グローバルに従う",
|
||||||
|
"settings.status_bar.theme_mode.dark": "ダーク",
|
||||||
|
"settings.status_bar.theme_mode.light": "ライト",
|
||||||
"settings.components.title": "コンポーネント",
|
"settings.components.title": "コンポーネント",
|
||||||
"settings.components.description": "コンポーネントのレイアウトとコーナーデザインを調整します。",
|
"settings.components.description": "コンポーネントのレイアウトとコーナーデザインを調整します。",
|
||||||
"settings.components.grid_header": "グリッド設定",
|
"settings.components.grid_header": "グリッド設定",
|
||||||
|
|||||||
@@ -389,6 +389,29 @@
|
|||||||
"settings.status_bar.text_capsule_position.right": "오른쪽",
|
"settings.status_bar.text_capsule_position.right": "오른쪽",
|
||||||
"settings.status_bar.text_capsule_content_label": "텍스트 내용 (Markdown 지원)",
|
"settings.status_bar.text_capsule_content_label": "텍스트 내용 (Markdown 지원)",
|
||||||
"settings.status_bar.text_capsule_transparent_background_label": "투명 배경",
|
"settings.status_bar.text_capsule_transparent_background_label": "투명 배경",
|
||||||
|
"settings.status_bar.network_speed_header": "네트워크 속도",
|
||||||
|
"settings.status_bar.network_speed_description": "상태 표시줄에 실시간 네트워크 속도를 표시합니다.",
|
||||||
|
"settings.status_bar.network_speed_position_label": "네트워크 속도 위치",
|
||||||
|
"settings.status_bar.network_speed_position.left": "왼쪽",
|
||||||
|
"settings.status_bar.network_speed_position.center": "가욍데",
|
||||||
|
"settings.status_bar.network_speed_position.right": "오른쪽",
|
||||||
|
"settings.status_bar.network_speed_mode_label": "표시 모드",
|
||||||
|
"settings.status_bar.network_speed_mode.both": "업로드 + 다운로드",
|
||||||
|
"settings.status_bar.network_speed_mode.upload": "업로드만",
|
||||||
|
"settings.status_bar.network_speed_mode.download": "다운로드만",
|
||||||
|
"settings.status_bar.network_speed_transparent_background_label": "투명 배경",
|
||||||
|
"settings.status_bar.show_network_type_icon_label": "네트워크 유형 아이콘 표시",
|
||||||
|
"settings.status_bar.shadow_header": "상태 표시줄 그림자",
|
||||||
|
"settings.status_bar.shadow_desc": "투명한 구성 요소의 가시성을 높이기 위해 상태 표시줄에 그림자 효과를 추가합니다.",
|
||||||
|
"settings.status_bar.shadow_enabled_label": "그림자 활성화",
|
||||||
|
"settings.status_bar.shadow_color_label": "그림자 색상",
|
||||||
|
"settings.status_bar.shadow_opacity_label": "그림자 불투명도",
|
||||||
|
"settings.status_bar.theme_header": "상태 표시줄 테마",
|
||||||
|
"settings.status_bar.theme_desc": "상태 표시줄의 테마 모드를 독립적으로 설정합니다.",
|
||||||
|
"settings.status_bar.theme_mode_label": "테마 모드",
|
||||||
|
"settings.status_bar.theme_mode.follow_global": "전역 따르기",
|
||||||
|
"settings.status_bar.theme_mode.dark": "다크",
|
||||||
|
"settings.status_bar.theme_mode.light": "라이트",
|
||||||
"settings.components.title": "컴포넌트",
|
"settings.components.title": "컴포넌트",
|
||||||
"settings.components.description": "컴포넌트 레이아웃과 모서리 디자인을 조정합니다.",
|
"settings.components.description": "컴포넌트 레이아웃과 모서리 디자인을 조정합니다.",
|
||||||
"settings.components.grid_header": "그리드 설정",
|
"settings.components.grid_header": "그리드 설정",
|
||||||
|
|||||||
@@ -395,6 +395,29 @@
|
|||||||
"settings.status_bar.text_capsule_position.right": "靠右",
|
"settings.status_bar.text_capsule_position.right": "靠右",
|
||||||
"settings.status_bar.text_capsule_content_label": "文字内容(支持 Markdown)",
|
"settings.status_bar.text_capsule_content_label": "文字内容(支持 Markdown)",
|
||||||
"settings.status_bar.text_capsule_transparent_background_label": "透明背景",
|
"settings.status_bar.text_capsule_transparent_background_label": "透明背景",
|
||||||
|
"settings.status_bar.network_speed_header": "网速显示",
|
||||||
|
"settings.status_bar.network_speed_description": "在状态栏显示实时网络上传和下载速度。",
|
||||||
|
"settings.status_bar.network_speed_position_label": "网速显示位置",
|
||||||
|
"settings.status_bar.network_speed_position.left": "靠左",
|
||||||
|
"settings.status_bar.network_speed_position.center": "居中",
|
||||||
|
"settings.status_bar.network_speed_position.right": "靠右",
|
||||||
|
"settings.status_bar.network_speed_mode_label": "显示模式",
|
||||||
|
"settings.status_bar.network_speed_mode.both": "上传 + 下载",
|
||||||
|
"settings.status_bar.network_speed_mode.upload": "仅上传",
|
||||||
|
"settings.status_bar.network_speed_mode.download": "仅下载",
|
||||||
|
"settings.status_bar.network_speed_transparent_background_label": "透明背景",
|
||||||
|
"settings.status_bar.show_network_type_icon_label": "显示网络类型图标",
|
||||||
|
"settings.status_bar.shadow_header": "状态栏阴影",
|
||||||
|
"settings.status_bar.shadow_desc": "为状态栏添加阴影效果,使透明背景的组件更清晰。",
|
||||||
|
"settings.status_bar.shadow_enabled_label": "启用阴影",
|
||||||
|
"settings.status_bar.shadow_color_label": "阴影颜色",
|
||||||
|
"settings.status_bar.shadow_opacity_label": "阴影透明度",
|
||||||
|
"settings.status_bar.theme_header": "状态栏主题",
|
||||||
|
"settings.status_bar.theme_desc": "独立设置状态栏的主题模式。",
|
||||||
|
"settings.status_bar.theme_mode_label": "主题模式",
|
||||||
|
"settings.status_bar.theme_mode.follow_global": "跟随全局",
|
||||||
|
"settings.status_bar.theme_mode.dark": "暗色",
|
||||||
|
"settings.status_bar.theme_mode.light": "浅色",
|
||||||
"settings.components.title": "组件",
|
"settings.components.title": "组件",
|
||||||
"settings.components.description": "调整组件布局与圆角设计。",
|
"settings.components.description": "调整组件布局与圆角设计。",
|
||||||
"settings.components.grid_header": "网格设置",
|
"settings.components.grid_header": "网格设置",
|
||||||
|
|||||||
@@ -114,6 +114,8 @@ public sealed class AppSettingsSnapshot
|
|||||||
|
|
||||||
public string ClockPosition { get; set; } = "Left"; // Left, Center, Right
|
public string ClockPosition { get; set; } = "Left"; // Left, Center, Right
|
||||||
|
|
||||||
|
public string ClockFontSize { get; set; } = "Medium"; // Small, Medium, Large
|
||||||
|
|
||||||
public bool ShowTextCapsule { get; set; } = false;
|
public bool ShowTextCapsule { get; set; } = false;
|
||||||
|
|
||||||
public string TextCapsuleContent { get; set; } = "**Hello** World!";
|
public string TextCapsuleContent { get; set; } = "**Hello** World!";
|
||||||
@@ -122,8 +124,28 @@ public sealed class AppSettingsSnapshot
|
|||||||
|
|
||||||
public bool TextCapsuleTransparentBackground { get; set; } = false;
|
public bool TextCapsuleTransparentBackground { get; set; } = false;
|
||||||
|
|
||||||
|
public string TextCapsuleFontSize { get; set; } = "Medium"; // Small, Medium, Large
|
||||||
|
|
||||||
|
public bool ShowNetworkSpeed { get; set; } = false;
|
||||||
|
|
||||||
|
public string NetworkSpeedPosition { get; set; } = "Right"; // Left, Center, Right
|
||||||
|
|
||||||
|
public string NetworkSpeedDisplayMode { get; set; } = "Both"; // Upload, Download, Both
|
||||||
|
|
||||||
|
public bool NetworkSpeedTransparentBackground { get; set; } = false;
|
||||||
|
|
||||||
|
public bool ShowNetworkTypeIcon { get; set; } = false;
|
||||||
|
|
||||||
|
public string NetworkSpeedFontSize { get; set; } = "Medium"; // Small, Medium, Large
|
||||||
|
|
||||||
public string StatusBarSpacingMode { get; set; } = "Relaxed";
|
public string StatusBarSpacingMode { get; set; } = "Relaxed";
|
||||||
|
|
||||||
|
public bool StatusBarShadowEnabled { get; set; } = false;
|
||||||
|
|
||||||
|
public string StatusBarShadowColor { get; set; } = "#000000";
|
||||||
|
|
||||||
|
public double StatusBarShadowOpacity { get; set; } = 0.3;
|
||||||
|
|
||||||
public int StatusBarCustomSpacingPercent { get; set; } = 12;
|
public int StatusBarCustomSpacingPercent { get; set; } = 12;
|
||||||
|
|
||||||
public bool EnableThreeFingerSwipe { get; set; } = false;
|
public bool EnableThreeFingerSwipe { get; set; } = false;
|
||||||
|
|||||||
@@ -135,6 +135,55 @@ public static class ZhiJiaoHubSources
|
|||||||
_ => ClassIsland
|
_ => ClassIsland
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetDisplayName(string source)
|
||||||
|
{
|
||||||
|
return source?.ToLowerInvariant() switch
|
||||||
|
{
|
||||||
|
Sectl => "SECTL 图库",
|
||||||
|
RinLit => "Rin's 图库",
|
||||||
|
_ => "ClassIsland 图库"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 智教Hub数据源配置
|
||||||
|
public sealed class ZhiJiaoHubSourceConfig
|
||||||
|
{
|
||||||
|
public string Owner { get; init; } = string.Empty;
|
||||||
|
public string Repo { get; init; } = string.Empty;
|
||||||
|
public string Path { get; init; } = string.Empty;
|
||||||
|
public string DisplayName { get; init; } = string.Empty;
|
||||||
|
public string ApiUrl => $"https://api.github.com/repos/{Owner}/{Repo}/contents/{Path}";
|
||||||
|
public string RawUrlTemplate => $"https://raw.githubusercontent.com/{Owner}/{Repo}/main/{Path}/{{0}}";
|
||||||
|
|
||||||
|
public static ZhiJiaoHubSourceConfig GetConfig(string source)
|
||||||
|
{
|
||||||
|
return source?.ToLowerInvariant() switch
|
||||||
|
{
|
||||||
|
ZhiJiaoHubSources.Sectl => new ZhiJiaoHubSourceConfig
|
||||||
|
{
|
||||||
|
Owner = "SECTL",
|
||||||
|
Repo = "SECTL-hub",
|
||||||
|
Path = "docs/.vuepress/public/images",
|
||||||
|
DisplayName = "SECTL 图库"
|
||||||
|
},
|
||||||
|
ZhiJiaoHubSources.RinLit => new ZhiJiaoHubSourceConfig
|
||||||
|
{
|
||||||
|
Owner = "RinLit-233-shiroko",
|
||||||
|
Repo = "Rin-sHub",
|
||||||
|
Path = "assets/images",
|
||||||
|
DisplayName = "Rin's 图库"
|
||||||
|
},
|
||||||
|
_ => new ZhiJiaoHubSourceConfig
|
||||||
|
{
|
||||||
|
Owner = "ClassIsland",
|
||||||
|
Repo = "classisland-hub",
|
||||||
|
Path = "images",
|
||||||
|
DisplayName = "ClassIsland 图库"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 智教Hub镜像加速源常量
|
// 智教Hub镜像加速源常量
|
||||||
|
|||||||
@@ -317,11 +317,13 @@ public sealed record RecommendationApiOptions
|
|||||||
|
|
||||||
public string ClassIslandHubApiUrl { get; init; } = "https://api.github.com/repos/ClassIsland/classisland-hub/contents/images";
|
public string ClassIslandHubApiUrl { get; init; } = "https://api.github.com/repos/ClassIsland/classisland-hub/contents/images";
|
||||||
|
|
||||||
public string SectlHubApiUrl { get; init; } = "https://api.github.com/repos/SECTL/SECTL-hub/contents/images";
|
public string SectlHubApiUrl { get; init; } = "https://api.github.com/repos/SECTL/SECTL-hub/contents/docs/.vuepress/public/images";
|
||||||
|
|
||||||
|
public string RinLitHubApiUrl { get; init; } = "https://api.github.com/repos/RinLit-233-shiroko/Rin-sHub/contents/images";
|
||||||
|
|
||||||
public string ClassIslandHubRawUrlTemplate { get; init; } = "https://raw.githubusercontent.com/ClassIsland/classisland-hub/main/images/{0}";
|
public string ClassIslandHubRawUrlTemplate { get; init; } = "https://raw.githubusercontent.com/ClassIsland/classisland-hub/main/images/{0}";
|
||||||
|
|
||||||
public string SectlHubRawUrlTemplate { get; init; } = "https://raw.githubusercontent.com/SECTL/SECTL-hub/main/images/{0}";
|
public string SectlHubRawUrlTemplate { get; init; } = "https://raw.githubusercontent.com/SECTL/SECTL-hub/main/docs/.vuepress/public/images/{0}";
|
||||||
|
|
||||||
public string RinLitHubRawUrlTemplate { get; init; } = "https://raw.githubusercontent.com/RinLit-233-shiroko/Rin-sHub/main/images/{0}";
|
public string RinLitHubRawUrlTemplate { get; init; } = "https://raw.githubusercontent.com/RinLit-233-shiroko/Rin-sHub/main/images/{0}";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3244,16 +3244,10 @@ public sealed class RecommendationDataService : IRecommendationInfoService, IDis
|
|||||||
|
|
||||||
private async Task<ZhiJiaoHubSnapshot> FetchZhiJiaoHubSnapshotAsync(string source, string mirrorSource, CancellationToken cancellationToken)
|
private async Task<ZhiJiaoHubSnapshot> FetchZhiJiaoHubSnapshotAsync(string source, string mirrorSource, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var (owner, repo, path) = source switch
|
var config = ZhiJiaoHubSourceConfig.GetConfig(source);
|
||||||
{
|
|
||||||
ZhiJiaoHubSources.Sectl => ("SECTL", "SECTL-hub", "docs/.vuepress/public/images"),
|
|
||||||
ZhiJiaoHubSources.RinLit => ("RinLit-233-shiroko", "Rin-sHub", "images"),
|
|
||||||
_ => ("ClassIsland", "classisland-hub", "images")
|
|
||||||
};
|
|
||||||
|
|
||||||
var contentsUrl = $"https://api.github.com/repos/{owner}/{repo}/contents/{path}";
|
var contentsUrl = config.ApiUrl;
|
||||||
|
|
||||||
// 如果使用镜像加速,代理 GitHub API 请求
|
|
||||||
if (string.Equals(mirrorSource, ZhiJiaoHubMirrorSources.GhProxy, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(mirrorSource, ZhiJiaoHubMirrorSources.GhProxy, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
contentsUrl = ZhiJiaoHubMirrorSources.GhProxyBaseUrl.TrimEnd('/') + "/" + contentsUrl;
|
contentsUrl = ZhiJiaoHubMirrorSources.GhProxyBaseUrl.TrimEnd('/') + "/" + contentsUrl;
|
||||||
@@ -3261,18 +3255,16 @@ public sealed class RecommendationDataService : IRecommendationInfoService, IDis
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var images = await FetchImagesFromContentsApi(owner, repo, path, contentsUrl, mirrorSource, cancellationToken);
|
var images = await FetchImagesFromContentsApi(config, contentsUrl, mirrorSource, cancellationToken);
|
||||||
|
|
||||||
if (images.Count == 0)
|
if (images.Count == 0)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("未找到图片文件");
|
throw new InvalidOperationException($"在 {config.DisplayName} 中未找到图片文件");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 随机打乱图片顺序
|
|
||||||
var random = new Random();
|
var random = new Random();
|
||||||
var shuffled = images.OrderBy(_ => random.Next()).ToList();
|
var shuffled = images.OrderBy(_ => random.Next()).ToList();
|
||||||
|
|
||||||
// 重新设置索引
|
|
||||||
for (int i = 0; i < shuffled.Count; i++)
|
for (int i = 0; i < shuffled.Count; i++)
|
||||||
{
|
{
|
||||||
var item = shuffled[i];
|
var item = shuffled[i];
|
||||||
@@ -3287,11 +3279,15 @@ public sealed class RecommendationDataService : IRecommendationInfoService, IDis
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
throw new HttpRequestException($"获取图片列表失败: {ex.Message}");
|
throw new HttpRequestException($"从 {config.DisplayName} 获取图片列表失败: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<List<ZhiJiaoHubImageItem>> FetchImagesFromContentsApi(string owner, string repo, string path, string contentsUrl, string mirrorSource, CancellationToken cancellationToken)
|
private async Task<List<ZhiJiaoHubImageItem>> FetchImagesFromContentsApi(
|
||||||
|
ZhiJiaoHubSourceConfig config,
|
||||||
|
string contentsUrl,
|
||||||
|
string mirrorSource,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var images = new List<ZhiJiaoHubImageItem>();
|
var images = new List<ZhiJiaoHubImageItem>();
|
||||||
|
|
||||||
@@ -3309,7 +3305,17 @@ public sealed class RecommendationDataService : IRecommendationInfoService, IDis
|
|||||||
{
|
{
|
||||||
throw new HttpRequestException("GitHub API 速率限制,请稍后重试");
|
throw new HttpRequestException("GitHub API 速率限制,请稍后重试");
|
||||||
}
|
}
|
||||||
throw new HttpRequestException($"API 返回错误: {(int)response.StatusCode} - {Truncate(errorText, 200)}");
|
|
||||||
|
if ((int)response.StatusCode == 404)
|
||||||
|
{
|
||||||
|
throw new HttpRequestException(
|
||||||
|
$"在 {config.DisplayName} 中找不到图片目录。请检查仓库结构和路径配置。\n" +
|
||||||
|
$"仓库: {config.Owner}/{config.Repo}\n" +
|
||||||
|
$"路径: {config.Path}");
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new HttpRequestException(
|
||||||
|
$"从 {config.DisplayName} 获取数据失败: {(int)response.StatusCode} - {Truncate(errorText, 200)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
var responseText = await response.Content.ReadAsStringAsync(cancellationToken);
|
var responseText = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||||
@@ -3321,9 +3327,9 @@ public sealed class RecommendationDataService : IRecommendationInfoService, IDis
|
|||||||
if (root.ValueKind == JsonValueKind.Object && root.TryGetProperty("message", out var messageNode))
|
if (root.ValueKind == JsonValueKind.Object && root.TryGetProperty("message", out var messageNode))
|
||||||
{
|
{
|
||||||
var errorMessage = messageNode.GetString();
|
var errorMessage = messageNode.GetString();
|
||||||
throw new InvalidOperationException($"GitHub API 错误: {errorMessage}");
|
throw new InvalidOperationException($"GitHub API 错误 ({config.DisplayName}): {errorMessage}");
|
||||||
}
|
}
|
||||||
throw new InvalidOperationException("Invalid response format from GitHub API.");
|
throw new InvalidOperationException($"从 {config.DisplayName} 返回的数据格式无效");
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
@@ -3343,18 +3349,15 @@ public sealed class RecommendationDataService : IRecommendationInfoService, IDis
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 只处理图片文件
|
|
||||||
var extension = Path.GetExtension(name).ToLowerInvariant();
|
var extension = Path.GetExtension(name).ToLowerInvariant();
|
||||||
if (extension != ".png" && extension != ".jpg" && extension != ".jpeg" && extension != ".gif" && extension != ".webp")
|
if (extension != ".png" && extension != ".jpg" && extension != ".jpeg" && extension != ".gif" && extension != ".webp")
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解码文件名
|
|
||||||
var decodedName = Uri.UnescapeDataString(name);
|
var decodedName = Uri.UnescapeDataString(name);
|
||||||
decodedName = Path.GetFileNameWithoutExtension(decodedName);
|
decodedName = Path.GetFileNameWithoutExtension(decodedName);
|
||||||
|
|
||||||
// 构造图片 URL
|
|
||||||
string imageUrl;
|
string imageUrl;
|
||||||
if (!string.IsNullOrWhiteSpace(downloadUrl))
|
if (!string.IsNullOrWhiteSpace(downloadUrl))
|
||||||
{
|
{
|
||||||
@@ -3362,10 +3365,12 @@ public sealed class RecommendationDataService : IRecommendationInfoService, IDis
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
imageUrl = $"https://raw.githubusercontent.com/{owner}/{repo}/main/{path}/{Uri.EscapeDataString(name)}";
|
imageUrl = string.Format(
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
config.RawUrlTemplate,
|
||||||
|
Uri.EscapeDataString(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 应用镜像加速到图片 URL
|
|
||||||
imageUrl = ZhiJiaoHubMirrorSources.ApplyMirror(imageUrl, mirrorSource);
|
imageUrl = ZhiJiaoHubMirrorSources.ApplyMirror(imageUrl, mirrorSource);
|
||||||
|
|
||||||
images.Add(new ZhiJiaoHubImageItem(decodedName, imageUrl, index));
|
images.Add(new ZhiJiaoHubImageItem(decodedName, imageUrl, index));
|
||||||
|
|||||||
@@ -42,12 +42,23 @@ public sealed record StatusBarSettingsState(
|
|||||||
string ClockDisplayFormat,
|
string ClockDisplayFormat,
|
||||||
bool ClockTransparentBackground,
|
bool ClockTransparentBackground,
|
||||||
string ClockPosition,
|
string ClockPosition,
|
||||||
|
string ClockFontSize,
|
||||||
bool ShowTextCapsule,
|
bool ShowTextCapsule,
|
||||||
string TextCapsuleContent,
|
string TextCapsuleContent,
|
||||||
string TextCapsulePosition,
|
string TextCapsulePosition,
|
||||||
bool TextCapsuleTransparentBackground,
|
bool TextCapsuleTransparentBackground,
|
||||||
|
string TextCapsuleFontSize,
|
||||||
|
bool ShowNetworkSpeed,
|
||||||
|
string NetworkSpeedPosition,
|
||||||
|
string NetworkSpeedDisplayMode,
|
||||||
|
bool NetworkSpeedTransparentBackground,
|
||||||
|
bool ShowNetworkTypeIcon,
|
||||||
|
string NetworkSpeedFontSize,
|
||||||
string SpacingMode,
|
string SpacingMode,
|
||||||
int CustomSpacingPercent);
|
int CustomSpacingPercent,
|
||||||
|
bool ShadowEnabled,
|
||||||
|
string ShadowColor,
|
||||||
|
double ShadowOpacity);
|
||||||
|
|
||||||
public sealed record TextCapsuleSettingsState(
|
public sealed record TextCapsuleSettingsState(
|
||||||
bool ShowTextCapsule,
|
bool ShowTextCapsule,
|
||||||
|
|||||||
@@ -387,12 +387,23 @@ internal sealed class StatusBarSettingsService : IStatusBarSettingsService
|
|||||||
snapshot.ClockDisplayFormat,
|
snapshot.ClockDisplayFormat,
|
||||||
snapshot.StatusBarClockTransparentBackground,
|
snapshot.StatusBarClockTransparentBackground,
|
||||||
snapshot.ClockPosition,
|
snapshot.ClockPosition,
|
||||||
|
snapshot.ClockFontSize,
|
||||||
snapshot.ShowTextCapsule,
|
snapshot.ShowTextCapsule,
|
||||||
snapshot.TextCapsuleContent,
|
snapshot.TextCapsuleContent,
|
||||||
snapshot.TextCapsulePosition,
|
snapshot.TextCapsulePosition,
|
||||||
snapshot.TextCapsuleTransparentBackground,
|
snapshot.TextCapsuleTransparentBackground,
|
||||||
|
snapshot.TextCapsuleFontSize,
|
||||||
|
snapshot.ShowNetworkSpeed,
|
||||||
|
snapshot.NetworkSpeedPosition,
|
||||||
|
snapshot.NetworkSpeedDisplayMode,
|
||||||
|
snapshot.NetworkSpeedTransparentBackground,
|
||||||
|
snapshot.ShowNetworkTypeIcon,
|
||||||
|
snapshot.NetworkSpeedFontSize,
|
||||||
snapshot.StatusBarSpacingMode,
|
snapshot.StatusBarSpacingMode,
|
||||||
snapshot.StatusBarCustomSpacingPercent);
|
snapshot.StatusBarCustomSpacingPercent,
|
||||||
|
snapshot.StatusBarShadowEnabled,
|
||||||
|
snapshot.StatusBarShadowColor,
|
||||||
|
snapshot.StatusBarShadowOpacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Save(StatusBarSettingsState state)
|
public void Save(StatusBarSettingsState state)
|
||||||
@@ -405,12 +416,23 @@ internal sealed class StatusBarSettingsService : IStatusBarSettingsService
|
|||||||
snapshot.ClockDisplayFormat = state.ClockDisplayFormat;
|
snapshot.ClockDisplayFormat = state.ClockDisplayFormat;
|
||||||
snapshot.StatusBarClockTransparentBackground = state.ClockTransparentBackground;
|
snapshot.StatusBarClockTransparentBackground = state.ClockTransparentBackground;
|
||||||
snapshot.ClockPosition = state.ClockPosition;
|
snapshot.ClockPosition = state.ClockPosition;
|
||||||
|
snapshot.ClockFontSize = state.ClockFontSize;
|
||||||
snapshot.ShowTextCapsule = state.ShowTextCapsule;
|
snapshot.ShowTextCapsule = state.ShowTextCapsule;
|
||||||
snapshot.TextCapsuleContent = state.TextCapsuleContent;
|
snapshot.TextCapsuleContent = state.TextCapsuleContent;
|
||||||
snapshot.TextCapsulePosition = state.TextCapsulePosition;
|
snapshot.TextCapsulePosition = state.TextCapsulePosition;
|
||||||
snapshot.TextCapsuleTransparentBackground = state.TextCapsuleTransparentBackground;
|
snapshot.TextCapsuleTransparentBackground = state.TextCapsuleTransparentBackground;
|
||||||
|
snapshot.TextCapsuleFontSize = state.TextCapsuleFontSize;
|
||||||
|
snapshot.ShowNetworkSpeed = state.ShowNetworkSpeed;
|
||||||
|
snapshot.NetworkSpeedPosition = state.NetworkSpeedPosition;
|
||||||
|
snapshot.NetworkSpeedDisplayMode = state.NetworkSpeedDisplayMode;
|
||||||
|
snapshot.NetworkSpeedTransparentBackground = state.NetworkSpeedTransparentBackground;
|
||||||
|
snapshot.ShowNetworkTypeIcon = state.ShowNetworkTypeIcon;
|
||||||
|
snapshot.NetworkSpeedFontSize = state.NetworkSpeedFontSize;
|
||||||
snapshot.StatusBarSpacingMode = state.SpacingMode;
|
snapshot.StatusBarSpacingMode = state.SpacingMode;
|
||||||
snapshot.StatusBarCustomSpacingPercent = state.CustomSpacingPercent;
|
snapshot.StatusBarCustomSpacingPercent = state.CustomSpacingPercent;
|
||||||
|
snapshot.StatusBarShadowEnabled = state.ShadowEnabled;
|
||||||
|
snapshot.StatusBarShadowColor = state.ShadowColor;
|
||||||
|
snapshot.StatusBarShadowOpacity = state.ShadowOpacity;
|
||||||
_settingsService.SaveSnapshot(
|
_settingsService.SaveSnapshot(
|
||||||
SettingsScope.App,
|
SettingsScope.App,
|
||||||
snapshot,
|
snapshot,
|
||||||
@@ -423,12 +445,23 @@ internal sealed class StatusBarSettingsService : IStatusBarSettingsService
|
|||||||
nameof(AppSettingsSnapshot.ClockDisplayFormat),
|
nameof(AppSettingsSnapshot.ClockDisplayFormat),
|
||||||
nameof(AppSettingsSnapshot.StatusBarClockTransparentBackground),
|
nameof(AppSettingsSnapshot.StatusBarClockTransparentBackground),
|
||||||
nameof(AppSettingsSnapshot.ClockPosition),
|
nameof(AppSettingsSnapshot.ClockPosition),
|
||||||
|
nameof(AppSettingsSnapshot.ClockFontSize),
|
||||||
nameof(AppSettingsSnapshot.ShowTextCapsule),
|
nameof(AppSettingsSnapshot.ShowTextCapsule),
|
||||||
nameof(AppSettingsSnapshot.TextCapsuleContent),
|
nameof(AppSettingsSnapshot.TextCapsuleContent),
|
||||||
nameof(AppSettingsSnapshot.TextCapsulePosition),
|
nameof(AppSettingsSnapshot.TextCapsulePosition),
|
||||||
nameof(AppSettingsSnapshot.TextCapsuleTransparentBackground),
|
nameof(AppSettingsSnapshot.TextCapsuleTransparentBackground),
|
||||||
|
nameof(AppSettingsSnapshot.TextCapsuleFontSize),
|
||||||
|
nameof(AppSettingsSnapshot.ShowNetworkSpeed),
|
||||||
|
nameof(AppSettingsSnapshot.NetworkSpeedPosition),
|
||||||
|
nameof(AppSettingsSnapshot.NetworkSpeedDisplayMode),
|
||||||
|
nameof(AppSettingsSnapshot.NetworkSpeedTransparentBackground),
|
||||||
|
nameof(AppSettingsSnapshot.ShowNetworkTypeIcon),
|
||||||
|
nameof(AppSettingsSnapshot.NetworkSpeedFontSize),
|
||||||
nameof(AppSettingsSnapshot.StatusBarSpacingMode),
|
nameof(AppSettingsSnapshot.StatusBarSpacingMode),
|
||||||
nameof(AppSettingsSnapshot.StatusBarCustomSpacingPercent)
|
nameof(AppSettingsSnapshot.StatusBarCustomSpacingPercent),
|
||||||
|
nameof(AppSettingsSnapshot.StatusBarShadowEnabled),
|
||||||
|
nameof(AppSettingsSnapshot.StatusBarShadowColor),
|
||||||
|
nameof(AppSettingsSnapshot.StatusBarShadowOpacity)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Avalonia.Media;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using LanMountainDesktop.ComponentSystem;
|
using LanMountainDesktop.ComponentSystem;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
@@ -22,7 +23,11 @@ public sealed partial class StatusBarSettingsPageViewModel : ViewModelBase
|
|||||||
|
|
||||||
ClockFormats = CreateClockFormats();
|
ClockFormats = CreateClockFormats();
|
||||||
ClockPositions = CreateClockPositions();
|
ClockPositions = CreateClockPositions();
|
||||||
|
ClockFontSizes = CreateFontSizes();
|
||||||
TextCapsulePositions = CreateTextCapsulePositions();
|
TextCapsulePositions = CreateTextCapsulePositions();
|
||||||
|
NetworkSpeedPositions = CreateNetworkSpeedPositions();
|
||||||
|
NetworkSpeedDisplayModes = CreateNetworkSpeedDisplayModes();
|
||||||
|
NetworkSpeedFontSizes = CreateFontSizes();
|
||||||
SpacingModes = CreateSpacingModes();
|
SpacingModes = CreateSpacingModes();
|
||||||
RefreshLocalizedText();
|
RefreshLocalizedText();
|
||||||
|
|
||||||
@@ -37,8 +42,16 @@ public sealed partial class StatusBarSettingsPageViewModel : ViewModelBase
|
|||||||
|
|
||||||
public IReadOnlyList<SelectionOption> TextCapsulePositions { get; }
|
public IReadOnlyList<SelectionOption> TextCapsulePositions { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<SelectionOption> NetworkSpeedPositions { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<SelectionOption> NetworkSpeedDisplayModes { get; }
|
||||||
|
|
||||||
public IReadOnlyList<SelectionOption> SpacingModes { get; }
|
public IReadOnlyList<SelectionOption> SpacingModes { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<SelectionOption> ClockFontSizes { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<SelectionOption> NetworkSpeedFontSizes { get; }
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _showClock = true;
|
private bool _showClock = true;
|
||||||
|
|
||||||
@@ -87,6 +100,12 @@ public sealed partial class StatusBarSettingsPageViewModel : ViewModelBase
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _clockPositionLabel = string.Empty;
|
private string _clockPositionLabel = string.Empty;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private SelectionOption _selectedClockFontSize = new("Medium", "Medium");
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _clockFontSizeLabel = string.Empty;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _textCapsuleHeader = string.Empty;
|
private string _textCapsuleHeader = string.Empty;
|
||||||
|
|
||||||
@@ -114,6 +133,45 @@ public sealed partial class StatusBarSettingsPageViewModel : ViewModelBase
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _textCapsuleTransparentBackgroundLabel = string.Empty;
|
private string _textCapsuleTransparentBackgroundLabel = string.Empty;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _networkSpeedHeader = string.Empty;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _networkSpeedDescription = string.Empty;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _showNetworkSpeed;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private SelectionOption _selectedNetworkSpeedPosition = new("Right", "Right");
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private SelectionOption _selectedNetworkSpeedDisplayMode = new("Both", "Both");
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _networkSpeedTransparentBackground;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _networkSpeedPositionLabel = string.Empty;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _networkSpeedDisplayModeLabel = string.Empty;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _networkSpeedTransparentBackgroundLabel = string.Empty;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _showNetworkTypeIcon;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _showNetworkTypeIconLabel = string.Empty;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private SelectionOption _selectedNetworkSpeedFontSize = new("Medium", "Medium");
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _networkSpeedFontSizeLabel = string.Empty;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _spacingHeader = string.Empty;
|
private string _spacingHeader = string.Empty;
|
||||||
|
|
||||||
@@ -123,6 +181,32 @@ public sealed partial class StatusBarSettingsPageViewModel : ViewModelBase
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _customSpacingLabel = string.Empty;
|
private string _customSpacingLabel = string.Empty;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _statusBarShadowEnabled;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private Color _statusBarShadowColor = Colors.Black;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private double _statusBarShadowOpacity = 30;
|
||||||
|
|
||||||
|
public IBrush StatusBarShadowColorBrush => new SolidColorBrush(StatusBarShadowColor);
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _statusBarShadowHeader = string.Empty;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _statusBarShadowDescription = string.Empty;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _statusBarShadowEnabledLabel = string.Empty;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _statusBarShadowColorLabel = string.Empty;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _statusBarShadowOpacityLabel = string.Empty;
|
||||||
|
|
||||||
public void Load()
|
public void Load()
|
||||||
{
|
{
|
||||||
var state = _settingsFacade.StatusBar.Get();
|
var state = _settingsFacade.StatusBar.Get();
|
||||||
@@ -143,6 +227,12 @@ public sealed partial class StatusBarSettingsPageViewModel : ViewModelBase
|
|||||||
string.Equals(option.Value, clockPosition, StringComparison.OrdinalIgnoreCase))
|
string.Equals(option.Value, clockPosition, StringComparison.OrdinalIgnoreCase))
|
||||||
?? ClockPositions[0];
|
?? ClockPositions[0];
|
||||||
|
|
||||||
|
// 时钟字体大小设置
|
||||||
|
var clockFontSize = NormalizeFontSize(state.ClockFontSize);
|
||||||
|
SelectedClockFontSize = ClockFontSizes.FirstOrDefault(option =>
|
||||||
|
string.Equals(option.Value, clockFontSize, StringComparison.OrdinalIgnoreCase))
|
||||||
|
?? ClockFontSizes[1]; // 默认中等
|
||||||
|
|
||||||
// 文字胶囊设置
|
// 文字胶囊设置
|
||||||
ShowTextCapsule = state.ShowTextCapsule;
|
ShowTextCapsule = state.ShowTextCapsule;
|
||||||
TextCapsuleContent = state.TextCapsuleContent ?? "**Hello** World!";
|
TextCapsuleContent = state.TextCapsuleContent ?? "**Hello** World!";
|
||||||
@@ -152,12 +242,39 @@ public sealed partial class StatusBarSettingsPageViewModel : ViewModelBase
|
|||||||
?? TextCapsulePositions[2]; // 默认靠右
|
?? TextCapsulePositions[2]; // 默认靠右
|
||||||
TextCapsuleTransparentBackground = state.TextCapsuleTransparentBackground;
|
TextCapsuleTransparentBackground = state.TextCapsuleTransparentBackground;
|
||||||
|
|
||||||
|
// 网速设置
|
||||||
|
ShowNetworkSpeed = state.ShowNetworkSpeed;
|
||||||
|
var networkSpeedPosition = NormalizeNetworkSpeedPosition(state.NetworkSpeedPosition);
|
||||||
|
SelectedNetworkSpeedPosition = NetworkSpeedPositions.FirstOrDefault(option =>
|
||||||
|
string.Equals(option.Value, networkSpeedPosition, StringComparison.OrdinalIgnoreCase))
|
||||||
|
?? NetworkSpeedPositions[2]; // 默认靠右
|
||||||
|
var networkSpeedDisplayMode = NormalizeNetworkSpeedDisplayMode(state.NetworkSpeedDisplayMode);
|
||||||
|
SelectedNetworkSpeedDisplayMode = NetworkSpeedDisplayModes.FirstOrDefault(option =>
|
||||||
|
string.Equals(option.Value, networkSpeedDisplayMode, StringComparison.OrdinalIgnoreCase))
|
||||||
|
?? NetworkSpeedDisplayModes[0]; // 默认双向
|
||||||
|
NetworkSpeedTransparentBackground = state.NetworkSpeedTransparentBackground;
|
||||||
|
ShowNetworkTypeIcon = state.ShowNetworkTypeIcon;
|
||||||
|
|
||||||
|
// 网速字体大小设置
|
||||||
|
var networkSpeedFontSize = NormalizeFontSize(state.NetworkSpeedFontSize);
|
||||||
|
SelectedNetworkSpeedFontSize = NetworkSpeedFontSizes.FirstOrDefault(option =>
|
||||||
|
string.Equals(option.Value, networkSpeedFontSize, StringComparison.OrdinalIgnoreCase))
|
||||||
|
?? NetworkSpeedFontSizes[1]; // 默认中等
|
||||||
|
|
||||||
var spacingMode = NormalizeSpacingMode(state.SpacingMode);
|
var spacingMode = NormalizeSpacingMode(state.SpacingMode);
|
||||||
SelectedSpacingMode = SpacingModes.FirstOrDefault(option =>
|
SelectedSpacingMode = SpacingModes.FirstOrDefault(option =>
|
||||||
string.Equals(option.Value, spacingMode, StringComparison.OrdinalIgnoreCase))
|
string.Equals(option.Value, spacingMode, StringComparison.OrdinalIgnoreCase))
|
||||||
?? SpacingModes[1];
|
?? SpacingModes[1];
|
||||||
CustomSpacingPercent = Math.Clamp(state.CustomSpacingPercent, 0, 30);
|
CustomSpacingPercent = Math.Clamp(state.CustomSpacingPercent, 0, 30);
|
||||||
IsCustomSpacingVisible = string.Equals(SelectedSpacingMode.Value, "Custom", StringComparison.OrdinalIgnoreCase);
|
IsCustomSpacingVisible = string.Equals(SelectedSpacingMode.Value, "Custom", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
// 状态栏阴影设置
|
||||||
|
StatusBarShadowEnabled = state.ShadowEnabled;
|
||||||
|
if (Color.TryParse(state.ShadowColor, out var shadowColor))
|
||||||
|
{
|
||||||
|
StatusBarShadowColor = shadowColor;
|
||||||
|
}
|
||||||
|
StatusBarShadowOpacity = Math.Clamp(state.ShadowOpacity * 100, 0, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnShowClockChanged(bool value)
|
partial void OnShowClockChanged(bool value)
|
||||||
@@ -200,6 +317,16 @@ public sealed partial class StatusBarSettingsPageViewModel : ViewModelBase
|
|||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
partial void OnSelectedClockFontSizeChanged(SelectionOption value)
|
||||||
|
{
|
||||||
|
if (_isInitializing || value is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
partial void OnShowTextCapsuleChanged(bool value)
|
partial void OnShowTextCapsuleChanged(bool value)
|
||||||
{
|
{
|
||||||
if (_isInitializing)
|
if (_isInitializing)
|
||||||
@@ -240,6 +367,66 @@ public sealed partial class StatusBarSettingsPageViewModel : ViewModelBase
|
|||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
partial void OnShowNetworkSpeedChanged(bool value)
|
||||||
|
{
|
||||||
|
if (_isInitializing)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnSelectedNetworkSpeedPositionChanged(SelectionOption value)
|
||||||
|
{
|
||||||
|
if (_isInitializing || value is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnSelectedNetworkSpeedDisplayModeChanged(SelectionOption value)
|
||||||
|
{
|
||||||
|
if (_isInitializing || value is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnNetworkSpeedTransparentBackgroundChanged(bool value)
|
||||||
|
{
|
||||||
|
if (_isInitializing)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnShowNetworkTypeIconChanged(bool value)
|
||||||
|
{
|
||||||
|
if (_isInitializing)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnSelectedNetworkSpeedFontSizeChanged(SelectionOption value)
|
||||||
|
{
|
||||||
|
if (_isInitializing || value is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
partial void OnSelectedSpacingModeChanged(SelectionOption value)
|
partial void OnSelectedSpacingModeChanged(SelectionOption value)
|
||||||
{
|
{
|
||||||
IsCustomSpacingVisible = string.Equals(value?.Value, "Custom", StringComparison.OrdinalIgnoreCase);
|
IsCustomSpacingVisible = string.Equals(value?.Value, "Custom", StringComparison.OrdinalIgnoreCase);
|
||||||
@@ -268,6 +455,37 @@ public sealed partial class StatusBarSettingsPageViewModel : ViewModelBase
|
|||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
partial void OnStatusBarShadowEnabledChanged(bool value)
|
||||||
|
{
|
||||||
|
if (_isInitializing)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnStatusBarShadowColorChanged(Color value)
|
||||||
|
{
|
||||||
|
OnPropertyChanged(nameof(StatusBarShadowColorBrush));
|
||||||
|
if (_isInitializing)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnStatusBarShadowOpacityChanged(double value)
|
||||||
|
{
|
||||||
|
if (_isInitializing)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
private void Save()
|
private void Save()
|
||||||
{
|
{
|
||||||
var state = _settingsFacade.StatusBar.Get();
|
var state = _settingsFacade.StatusBar.Get();
|
||||||
@@ -288,12 +506,23 @@ public sealed partial class StatusBarSettingsPageViewModel : ViewModelBase
|
|||||||
SelectedClockFormat.Value,
|
SelectedClockFormat.Value,
|
||||||
ClockTransparentBackground,
|
ClockTransparentBackground,
|
||||||
SelectedClockPosition.Value,
|
SelectedClockPosition.Value,
|
||||||
|
SelectedClockFontSize?.Value ?? "Medium",
|
||||||
ShowTextCapsule,
|
ShowTextCapsule,
|
||||||
TextCapsuleContent ?? "**Hello** World!",
|
TextCapsuleContent ?? "**Hello** World!",
|
||||||
SelectedTextCapsulePosition?.Value ?? "Right",
|
SelectedTextCapsulePosition?.Value ?? "Right",
|
||||||
TextCapsuleTransparentBackground,
|
TextCapsuleTransparentBackground,
|
||||||
|
"Medium", // TextCapsuleFontSize - 暂时使用默认值
|
||||||
|
ShowNetworkSpeed,
|
||||||
|
SelectedNetworkSpeedPosition?.Value ?? "Right",
|
||||||
|
SelectedNetworkSpeedDisplayMode?.Value ?? "Both",
|
||||||
|
NetworkSpeedTransparentBackground,
|
||||||
|
ShowNetworkTypeIcon,
|
||||||
|
SelectedNetworkSpeedFontSize?.Value ?? "Medium",
|
||||||
NormalizeSpacingMode(SelectedSpacingMode.Value),
|
NormalizeSpacingMode(SelectedSpacingMode.Value),
|
||||||
Math.Clamp(CustomSpacingPercent, 0, 30)));
|
Math.Clamp(CustomSpacingPercent, 0, 30),
|
||||||
|
StatusBarShadowEnabled,
|
||||||
|
StatusBarShadowColor.ToString(),
|
||||||
|
StatusBarShadowOpacity / 100.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private IReadOnlyList<SelectionOption> CreateClockFormats()
|
private IReadOnlyList<SelectionOption> CreateClockFormats()
|
||||||
@@ -325,6 +554,26 @@ public sealed partial class StatusBarSettingsPageViewModel : ViewModelBase
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IReadOnlyList<SelectionOption> CreateNetworkSpeedPositions()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
[
|
||||||
|
new SelectionOption("Left", L("settings.status_bar.network_speed_position.left", "Left")),
|
||||||
|
new SelectionOption("Center", L("settings.status_bar.network_speed_position.center", "Center")),
|
||||||
|
new SelectionOption("Right", L("settings.status_bar.network_speed_position.right", "Right"))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private IReadOnlyList<SelectionOption> CreateNetworkSpeedDisplayModes()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
[
|
||||||
|
new SelectionOption("Both", L("settings.status_bar.network_speed_mode.both", "Upload + Download")),
|
||||||
|
new SelectionOption("Upload", L("settings.status_bar.network_speed_mode.upload", "Upload only")),
|
||||||
|
new SelectionOption("Download", L("settings.status_bar.network_speed_mode.download", "Download only"))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
private IReadOnlyList<SelectionOption> CreateSpacingModes()
|
private IReadOnlyList<SelectionOption> CreateSpacingModes()
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
@@ -346,14 +595,27 @@ public sealed partial class StatusBarSettingsPageViewModel : ViewModelBase
|
|||||||
ClockTransparentBackgroundLabel = L("settings.status_bar.clock_transparent_background_label", "Transparent background");
|
ClockTransparentBackgroundLabel = L("settings.status_bar.clock_transparent_background_label", "Transparent background");
|
||||||
ClockTransparentBackgroundDescription = L("settings.status_bar.clock_transparent_background_desc", "Remove the capsule background and keep only the clock text.");
|
ClockTransparentBackgroundDescription = L("settings.status_bar.clock_transparent_background_desc", "Remove the capsule background and keep only the clock text.");
|
||||||
ClockPositionLabel = L("settings.status_bar.clock_position_label", "Clock position");
|
ClockPositionLabel = L("settings.status_bar.clock_position_label", "Clock position");
|
||||||
|
ClockFontSizeLabel = L("settings.status_bar.clock_font_size_label", "Font size");
|
||||||
TextCapsuleHeader = L("settings.status_bar.text_capsule_header", "Text Capsule");
|
TextCapsuleHeader = L("settings.status_bar.text_capsule_header", "Text Capsule");
|
||||||
TextCapsuleDescription = L("settings.status_bar.text_capsule_description", "Display custom text with Markdown support on the status bar.");
|
TextCapsuleDescription = L("settings.status_bar.text_capsule_description", "Display custom text with Markdown support on the status bar.");
|
||||||
TextCapsulePositionLabel = L("settings.status_bar.text_capsule_position_label", "Text capsule position");
|
TextCapsulePositionLabel = L("settings.status_bar.text_capsule_position_label", "Text capsule position");
|
||||||
TextCapsuleContentLabel = L("settings.status_bar.text_capsule_content_label", "Text content (Markdown supported)");
|
TextCapsuleContentLabel = L("settings.status_bar.text_capsule_content_label", "Text content (Markdown supported)");
|
||||||
TextCapsuleTransparentBackgroundLabel = L("settings.status_bar.text_capsule_transparent_background_label", "Transparent background");
|
TextCapsuleTransparentBackgroundLabel = L("settings.status_bar.text_capsule_transparent_background_label", "Transparent background");
|
||||||
|
NetworkSpeedHeader = L("settings.status_bar.network_speed_header", "Network Speed");
|
||||||
|
NetworkSpeedDescription = L("settings.status_bar.network_speed_description", "Display real-time network upload and download speed.");
|
||||||
|
NetworkSpeedPositionLabel = L("settings.status_bar.network_speed_position_label", "Network speed position");
|
||||||
|
NetworkSpeedDisplayModeLabel = L("settings.status_bar.network_speed_mode_label", "Display mode");
|
||||||
|
NetworkSpeedTransparentBackgroundLabel = L("settings.status_bar.network_speed_transparent_background_label", "Transparent background");
|
||||||
|
ShowNetworkTypeIconLabel = L("settings.status_bar.show_network_type_icon_label", "Show network type icon");
|
||||||
|
NetworkSpeedFontSizeLabel = L("settings.status_bar.network_speed_font_size_label", "Font size");
|
||||||
SpacingHeader = L("settings.status_bar.spacing_header", "Component Spacing");
|
SpacingHeader = L("settings.status_bar.spacing_header", "Component Spacing");
|
||||||
SpacingDescription = L("settings.status_bar.spacing_desc", "Adjust spacing between status bar components.");
|
SpacingDescription = L("settings.status_bar.spacing_desc", "Adjust spacing between status bar components.");
|
||||||
CustomSpacingLabel = L("settings.status_bar.spacing_custom_label", "Custom spacing (%)");
|
CustomSpacingLabel = L("settings.status_bar.spacing_custom_label", "Custom spacing (%)");
|
||||||
|
StatusBarShadowHeader = L("settings.status_bar.shadow_header", "Status Bar Shadow");
|
||||||
|
StatusBarShadowDescription = L("settings.status_bar.shadow_desc", "Add shadow effect to the status bar for better visibility.");
|
||||||
|
StatusBarShadowEnabledLabel = L("settings.status_bar.shadow_enabled_label", "Enable shadow");
|
||||||
|
StatusBarShadowColorLabel = L("settings.status_bar.shadow_color_label", "Shadow color");
|
||||||
|
StatusBarShadowOpacityLabel = L("settings.status_bar.shadow_opacity_label", "Shadow opacity");
|
||||||
}
|
}
|
||||||
|
|
||||||
private string NormalizeSpacingMode(string? value)
|
private string NormalizeSpacingMode(string? value)
|
||||||
@@ -386,6 +648,46 @@ public sealed partial class StatusBarSettingsPageViewModel : ViewModelBase
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string NormalizeNetworkSpeedPosition(string? value)
|
||||||
|
{
|
||||||
|
return value switch
|
||||||
|
{
|
||||||
|
_ when string.Equals(value, "Left", StringComparison.OrdinalIgnoreCase) => "Left",
|
||||||
|
_ when string.Equals(value, "Center", StringComparison.OrdinalIgnoreCase) => "Center",
|
||||||
|
_ => "Right"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeNetworkSpeedDisplayMode(string? value)
|
||||||
|
{
|
||||||
|
return value switch
|
||||||
|
{
|
||||||
|
_ when string.Equals(value, "Upload", StringComparison.OrdinalIgnoreCase) => "Upload",
|
||||||
|
_ when string.Equals(value, "Download", StringComparison.OrdinalIgnoreCase) => "Download",
|
||||||
|
_ => "Both"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeFontSize(string? value)
|
||||||
|
{
|
||||||
|
return value switch
|
||||||
|
{
|
||||||
|
_ when string.Equals(value, "Small", StringComparison.OrdinalIgnoreCase) => "Small",
|
||||||
|
_ when string.Equals(value, "Large", StringComparison.OrdinalIgnoreCase) => "Large",
|
||||||
|
_ => "Medium"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private IReadOnlyList<SelectionOption> CreateFontSizes()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
[
|
||||||
|
new SelectionOption("Small", L("settings.status_bar.font_size.small", "Small")),
|
||||||
|
new SelectionOption("Medium", L("settings.status_bar.font_size.medium", "Medium")),
|
||||||
|
new SelectionOption("Large", L("settings.status_bar.font_size.large", "Large"))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
private string L(string key, string fallback)
|
private string L(string key, string fallback)
|
||||||
=> _localizationService.GetString(_languageCode, key, fallback);
|
=> _localizationService.GetString(_languageCode, key, fallback);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
@@ -25,6 +25,7 @@ public partial class ClockWidget : UserControl, IDesktopComponentWidget, ITimeZo
|
|||||||
private ClockDisplayFormat _displayFormat = ClockDisplayFormat.HourMinuteSecond;
|
private ClockDisplayFormat _displayFormat = ClockDisplayFormat.HourMinuteSecond;
|
||||||
private bool _transparentBackground;
|
private bool _transparentBackground;
|
||||||
private double _lastAppliedCellSize = 100;
|
private double _lastAppliedCellSize = 100;
|
||||||
|
private string _fontSize = "Medium"; // Small, Medium, Large
|
||||||
|
|
||||||
public ClockWidget()
|
public ClockWidget()
|
||||||
{
|
{
|
||||||
@@ -72,6 +73,21 @@ public partial class ClockWidget : UserControl, IDesktopComponentWidget, ITimeZo
|
|||||||
TransparentBackground = transparentBackground;
|
TransparentBackground = transparentBackground;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string WidgetFontSize
|
||||||
|
{
|
||||||
|
get => _fontSize;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_fontSize = value;
|
||||||
|
ApplyCellSize(_lastAppliedCellSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFontSize(string fontSize)
|
||||||
|
{
|
||||||
|
WidgetFontSize = fontSize;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetTimeZoneService(TimeZoneService timeZoneService)
|
public void SetTimeZoneService(TimeZoneService timeZoneService)
|
||||||
{
|
{
|
||||||
ClearTimeZoneService();
|
ClearTimeZoneService();
|
||||||
@@ -138,7 +154,14 @@ public partial class ClockWidget : UserControl, IDesktopComponentWidget, ITimeZo
|
|||||||
|
|
||||||
// 3. 核心:满盈字阶 (Filled Typography)
|
// 3. 核心:满盈字阶 (Filled Typography)
|
||||||
// 使主时间文字占据容器高度的 ~68%,产生饱满的视觉张力
|
// 使主时间文字占据容器高度的 ~68%,产生饱满的视觉张力
|
||||||
var mainFontSize = targetHeight * 0.68;
|
// 根据字体大小设置调整基础大小
|
||||||
|
var fontSizeMultiplier = _fontSize switch
|
||||||
|
{
|
||||||
|
"Small" => 0.55,
|
||||||
|
"Large" => 0.85,
|
||||||
|
_ => 0.68 // Medium (default)
|
||||||
|
};
|
||||||
|
var mainFontSize = targetHeight * fontSizeMultiplier;
|
||||||
MainTimeTextBlock.FontSize = mainFontSize;
|
MainTimeTextBlock.FontSize = mainFontSize;
|
||||||
MainTimeTextBlock.FontWeight = FontWeight.SemiBold;
|
MainTimeTextBlock.FontWeight = FontWeight.SemiBold;
|
||||||
|
|
||||||
|
|||||||
72
LanMountainDesktop/Views/Components/NetworkSpeedWidget.axaml
Normal file
72
LanMountainDesktop/Views/Components/NetworkSpeedWidget.axaml
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:fi="using:FluentIcons.Avalonia"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignWidth="160"
|
||||||
|
d:DesignHeight="48"
|
||||||
|
x:Class="LanMountainDesktop.Views.Components.NetworkSpeedWidget">
|
||||||
|
|
||||||
|
<Border x:Name="RootBorder"
|
||||||
|
Classes="surface-translucent-panel"
|
||||||
|
Padding="0"
|
||||||
|
CornerRadius="{DynamicResource DesignCornerRadiusComponent}">
|
||||||
|
<StackPanel Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="12,0">
|
||||||
|
<!-- 上传速度 -->
|
||||||
|
<StackPanel x:Name="UploadPanel"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<TextBlock Text="↑"
|
||||||
|
FontSize="12"
|
||||||
|
Opacity="0.7"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"/>
|
||||||
|
<TextBlock x:Name="UploadSpeedTextBlock"
|
||||||
|
FontSize="14"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Margin="2,0,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- 分隔符 -->
|
||||||
|
<Rectangle x:Name="Separator"
|
||||||
|
Width="1"
|
||||||
|
Height="16"
|
||||||
|
Margin="8,0"
|
||||||
|
Opacity="0.3"
|
||||||
|
Fill="{DynamicResource AdaptiveTextSecondaryBrush}"/>
|
||||||
|
|
||||||
|
<!-- 下载速度 -->
|
||||||
|
<StackPanel x:Name="DownloadPanel"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<TextBlock Text="↓"
|
||||||
|
FontSize="12"
|
||||||
|
Opacity="0.7"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"/>
|
||||||
|
<TextBlock x:Name="DownloadSpeedTextBlock"
|
||||||
|
FontSize="14"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Margin="2,0,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- 网络类型图标 -->
|
||||||
|
<fi:SymbolIcon x:Name="NetworkTypeIcon"
|
||||||
|
Symbol="Globe"
|
||||||
|
FontSize="14"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Opacity="0.8"
|
||||||
|
IsVisible="False"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
</UserControl>
|
||||||
451
LanMountainDesktop/Views/Components/NetworkSpeedWidget.axaml.cs
Normal file
451
LanMountainDesktop/Views/Components/NetworkSpeedWidget.axaml.cs
Normal file
@@ -0,0 +1,451 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
using FluentIcons.Avalonia;
|
||||||
|
using FluentIcons.Common;
|
||||||
|
using LanMountainDesktop.Services;
|
||||||
|
using Symbol = FluentIcons.Common.Symbol;
|
||||||
|
|
||||||
|
namespace LanMountainDesktop.Views.Components;
|
||||||
|
|
||||||
|
public partial class NetworkSpeedWidget : UserControl, IDesktopComponentWidget
|
||||||
|
{
|
||||||
|
private readonly DispatcherTimer _timer = new();
|
||||||
|
private readonly DispatcherTimer _networkTypeTimer = new();
|
||||||
|
private NetworkInterface? _selectedInterface;
|
||||||
|
private long _lastBytesReceived;
|
||||||
|
private long _lastBytesSent;
|
||||||
|
private bool _isFirstUpdate = true;
|
||||||
|
private double _lastAppliedCellSize = 100;
|
||||||
|
private bool _transparentBackground;
|
||||||
|
private string _displayMode = "Both"; // "Upload", "Download", "Both"
|
||||||
|
private bool _showNetworkTypeIcon;
|
||||||
|
private string _fontSize = "Medium"; // Small, Medium, Large
|
||||||
|
|
||||||
|
public NetworkSpeedWidget()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
SetupTimer();
|
||||||
|
SelectBestInterface();
|
||||||
|
UpdateDisplayMode();
|
||||||
|
UpdateNetworkTypeIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DisplayMode
|
||||||
|
{
|
||||||
|
get => _displayMode;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_displayMode == value) return;
|
||||||
|
_displayMode = value;
|
||||||
|
UpdateDisplayMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TransparentBackground
|
||||||
|
{
|
||||||
|
get => _transparentBackground;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_transparentBackground == value) return;
|
||||||
|
_transparentBackground = value;
|
||||||
|
ApplyChrome();
|
||||||
|
ApplyCellSize(_lastAppliedCellSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShowNetworkTypeIcon
|
||||||
|
{
|
||||||
|
get => _showNetworkTypeIcon;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_showNetworkTypeIcon == value) return;
|
||||||
|
_showNetworkTypeIcon = value;
|
||||||
|
UpdateNetworkTypeIcon();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetDisplayMode(string mode)
|
||||||
|
{
|
||||||
|
DisplayMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTransparentBackground(bool transparent)
|
||||||
|
{
|
||||||
|
TransparentBackground = transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetShowNetworkTypeIcon(bool show)
|
||||||
|
{
|
||||||
|
ShowNetworkTypeIcon = show;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string WidgetFontSize
|
||||||
|
{
|
||||||
|
get => _fontSize;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_fontSize = value;
|
||||||
|
ApplyCellSize(_lastAppliedCellSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFontSize(string fontSize)
|
||||||
|
{
|
||||||
|
WidgetFontSize = fontSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupTimer()
|
||||||
|
{
|
||||||
|
// 网速更新定时器(每秒)
|
||||||
|
_timer.Interval = TimeSpan.FromSeconds(1);
|
||||||
|
_timer.Tick += (_, _) => UpdateSpeed();
|
||||||
|
_timer.Start();
|
||||||
|
|
||||||
|
// 网络类型检测定时器(每500ms,满足响应延迟要求)
|
||||||
|
_networkTypeTimer.Interval = TimeSpan.FromMilliseconds(500);
|
||||||
|
_networkTypeTimer.Tick += (_, _) => UpdateNetworkTypeIcon();
|
||||||
|
_networkTypeTimer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectBestInterface()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var interfaces = NetworkInterface.GetAllNetworkInterfaces()
|
||||||
|
.Where(ni => ni.OperationalStatus == OperationalStatus.Up)
|
||||||
|
.Where(ni => ni.NetworkInterfaceType != NetworkInterfaceType.Loopback)
|
||||||
|
.Where(ni => ni.NetworkInterfaceType != NetworkInterfaceType.Tunnel)
|
||||||
|
.Where(ni => !ni.Description.Contains("Virtual", StringComparison.OrdinalIgnoreCase))
|
||||||
|
.Where(ni => !ni.Description.Contains("VPN", StringComparison.OrdinalIgnoreCase))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// 优先选择有流量的物理网卡
|
||||||
|
_selectedInterface = interfaces
|
||||||
|
.OrderByDescending(ni => ni.GetIPv4Statistics().BytesReceived + ni.GetIPv4Statistics().BytesSent)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
// 如果没有找到,选择第一个活动的非虚拟网卡
|
||||||
|
_selectedInterface ??= interfaces.FirstOrDefault();
|
||||||
|
|
||||||
|
if (_selectedInterface != null)
|
||||||
|
{
|
||||||
|
var stats = _selectedInterface.GetIPv4Statistics();
|
||||||
|
_lastBytesReceived = stats.BytesReceived;
|
||||||
|
_lastBytesSent = stats.BytesSent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 忽略错误,下次重试
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSpeed()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 如果当前网卡不可用,尝试重新选择
|
||||||
|
if (_selectedInterface == null ||
|
||||||
|
_selectedInterface.OperationalStatus != OperationalStatus.Up)
|
||||||
|
{
|
||||||
|
SelectBestInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_selectedInterface == null)
|
||||||
|
{
|
||||||
|
UploadSpeedTextBlock.Text = "--";
|
||||||
|
DownloadSpeedTextBlock.Text = "--";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var stats = _selectedInterface.GetIPv4Statistics();
|
||||||
|
var currentBytesReceived = stats.BytesReceived;
|
||||||
|
var currentBytesSent = stats.BytesSent;
|
||||||
|
|
||||||
|
if (_isFirstUpdate)
|
||||||
|
{
|
||||||
|
_lastBytesReceived = currentBytesReceived;
|
||||||
|
_lastBytesSent = currentBytesSent;
|
||||||
|
_isFirstUpdate = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算速度(每秒字节数)
|
||||||
|
var downloadBytes = currentBytesReceived - _lastBytesReceived;
|
||||||
|
var uploadBytes = currentBytesSent - _lastBytesSent;
|
||||||
|
|
||||||
|
// 处理计数器重置的情况
|
||||||
|
if (downloadBytes < 0) downloadBytes = 0;
|
||||||
|
if (uploadBytes < 0) uploadBytes = 0;
|
||||||
|
|
||||||
|
UploadSpeedTextBlock.Text = FormatSpeed(uploadBytes);
|
||||||
|
DownloadSpeedTextBlock.Text = FormatSpeed(downloadBytes);
|
||||||
|
|
||||||
|
_lastBytesReceived = currentBytesReceived;
|
||||||
|
_lastBytesSent = currentBytesSent;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 错误时显示 --
|
||||||
|
UploadSpeedTextBlock.Text = "--";
|
||||||
|
DownloadSpeedTextBlock.Text = "--";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateNetworkTypeIcon()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!_showNetworkTypeIcon || NetworkTypeIcon == null)
|
||||||
|
{
|
||||||
|
if (NetworkTypeIcon != null)
|
||||||
|
NetworkTypeIcon.IsVisible = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前活动的网络接口
|
||||||
|
var activeInterface = GetActiveNetworkInterface();
|
||||||
|
|
||||||
|
if (activeInterface == null)
|
||||||
|
{
|
||||||
|
// 无网络连接
|
||||||
|
NetworkTypeIcon.Symbol = Symbol.DismissCircle;
|
||||||
|
NetworkTypeIcon.IsVisible = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据网络类型设置图标
|
||||||
|
switch (activeInterface.NetworkInterfaceType)
|
||||||
|
{
|
||||||
|
case NetworkInterfaceType.Wireless80211:
|
||||||
|
// WiFi
|
||||||
|
NetworkTypeIcon.Symbol = Symbol.WiFi;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NetworkInterfaceType.Ethernet:
|
||||||
|
// 有线网络 - 检查是否是移动网络热点
|
||||||
|
if (IsLikelyMobileHotspot(activeInterface))
|
||||||
|
{
|
||||||
|
NetworkTypeIcon.Symbol = Symbol.Phone;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NetworkTypeIcon.Symbol = Symbol.PlugConnected;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// 其他类型,尝试根据描述判断
|
||||||
|
var symbol = GetSymbolFromDescription(activeInterface.Description);
|
||||||
|
NetworkTypeIcon.Symbol = symbol;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkTypeIcon.IsVisible = true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 错误时隐藏图标
|
||||||
|
if (NetworkTypeIcon != null)
|
||||||
|
NetworkTypeIcon.IsVisible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private NetworkInterface? GetActiveNetworkInterface()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 优先使用当前选中的网卡
|
||||||
|
if (_selectedInterface != null &&
|
||||||
|
_selectedInterface.OperationalStatus == OperationalStatus.Up)
|
||||||
|
{
|
||||||
|
return _selectedInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 否则查找最佳网卡
|
||||||
|
var interfaces = NetworkInterface.GetAllNetworkInterfaces()
|
||||||
|
.Where(ni => ni.OperationalStatus == OperationalStatus.Up)
|
||||||
|
.Where(ni => ni.NetworkInterfaceType != NetworkInterfaceType.Loopback)
|
||||||
|
.Where(ni => ni.NetworkInterfaceType != NetworkInterfaceType.Tunnel)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// 优先返回有流量的网卡
|
||||||
|
return interfaces
|
||||||
|
.OrderByDescending(ni => ni.GetIPv4Statistics().BytesReceived + ni.GetIPv4Statistics().BytesSent)
|
||||||
|
.FirstOrDefault();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsLikelyMobileHotspot(NetworkInterface ni)
|
||||||
|
{
|
||||||
|
// 通过描述判断是否是移动热点
|
||||||
|
var desc = ni.Description.ToLowerInvariant();
|
||||||
|
return desc.Contains("mobile") ||
|
||||||
|
desc.Contains("cellular") ||
|
||||||
|
desc.Contains("phone") ||
|
||||||
|
desc.Contains("tether");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Symbol GetSymbolFromDescription(string description)
|
||||||
|
{
|
||||||
|
var desc = description.ToLowerInvariant();
|
||||||
|
|
||||||
|
if (desc.Contains("wifi") || desc.Contains("wi-fi") || desc.Contains("wireless"))
|
||||||
|
return Symbol.WiFi;
|
||||||
|
|
||||||
|
if (desc.Contains("ethernet") || desc.Contains("lan") || desc.Contains("wired"))
|
||||||
|
return Symbol.PlugConnected;
|
||||||
|
|
||||||
|
if (desc.Contains("cellular") || desc.Contains("mobile") || desc.Contains("lte") || desc.Contains("5g") || desc.Contains("4g"))
|
||||||
|
return Symbol.Phone;
|
||||||
|
|
||||||
|
if (desc.Contains("bluetooth"))
|
||||||
|
return Symbol.Bluetooth;
|
||||||
|
|
||||||
|
// 默认使用 Globe 图标
|
||||||
|
return Symbol.Globe;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatSpeed(long bytesPerSecond)
|
||||||
|
{
|
||||||
|
// 根据数值大小决定显示格式,始终保持3个字符宽度
|
||||||
|
// 例如: 1.23, 12.3, 123
|
||||||
|
return bytesPerSecond switch
|
||||||
|
{
|
||||||
|
>= 1024 * 1024 * 1024 => FormatWithThreeDigits(bytesPerSecond / (1024.0 * 1024.0 * 1024.0), "G"),
|
||||||
|
>= 1024 * 1024 => FormatWithThreeDigits(bytesPerSecond / (1024.0 * 1024.0), "M"),
|
||||||
|
>= 1024 => FormatWithThreeDigits(bytesPerSecond / 1024.0, "K"),
|
||||||
|
_ => FormatWithThreeDigits(bytesPerSecond, "B")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 格式化数字,始终保持3个有效数字的显示宽度
|
||||||
|
/// </summary>
|
||||||
|
private static string FormatWithThreeDigits(double value, string unit)
|
||||||
|
{
|
||||||
|
// 根据数值大小决定小数位数,确保总宽度一致
|
||||||
|
// < 10: 显示两位小数 (如 1.23)
|
||||||
|
// 10-99: 显示一位小数 (如 12.3)
|
||||||
|
// >= 100: 显示整数 (如 123)
|
||||||
|
string formatted = value switch
|
||||||
|
{
|
||||||
|
< 10 => $"{value:F2}",
|
||||||
|
< 100 => $"{value:F1}",
|
||||||
|
_ => $"{value:F0}"
|
||||||
|
};
|
||||||
|
|
||||||
|
return formatted + unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateDisplayMode()
|
||||||
|
{
|
||||||
|
switch (_displayMode)
|
||||||
|
{
|
||||||
|
case "Upload":
|
||||||
|
UploadPanel.IsVisible = true;
|
||||||
|
DownloadPanel.IsVisible = false;
|
||||||
|
Separator.IsVisible = false;
|
||||||
|
break;
|
||||||
|
case "Download":
|
||||||
|
UploadPanel.IsVisible = false;
|
||||||
|
DownloadPanel.IsVisible = true;
|
||||||
|
Separator.IsVisible = false;
|
||||||
|
break;
|
||||||
|
case "Both":
|
||||||
|
default:
|
||||||
|
UploadPanel.IsVisible = true;
|
||||||
|
DownloadPanel.IsVisible = true;
|
||||||
|
Separator.IsVisible = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyCellSize(double cellSize)
|
||||||
|
{
|
||||||
|
_lastAppliedCellSize = cellSize;
|
||||||
|
|
||||||
|
// 计算组件高度:保持与任务栏核心比例一致 (0.74x)
|
||||||
|
var targetHeight = Math.Clamp(cellSize * 0.74, 34, 74);
|
||||||
|
RootBorder.Height = targetHeight;
|
||||||
|
|
||||||
|
// 主矩形统一到主题主档圆角
|
||||||
|
RootBorder.CornerRadius = ResolveUnifiedMainRectangle();
|
||||||
|
RootBorder.VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center;
|
||||||
|
|
||||||
|
// 根据单元格大小和字体大小设置调整字体大小
|
||||||
|
var fontSizeMultiplier = _fontSize switch
|
||||||
|
{
|
||||||
|
"Small" => 0.32,
|
||||||
|
"Large" => 0.48,
|
||||||
|
_ => 0.4 // Medium (default)
|
||||||
|
};
|
||||||
|
var fontSize = Math.Clamp(targetHeight * fontSizeMultiplier, 11, 22);
|
||||||
|
UploadSpeedTextBlock.FontSize = fontSize;
|
||||||
|
DownloadSpeedTextBlock.FontSize = fontSize;
|
||||||
|
|
||||||
|
// 调整图标大小
|
||||||
|
if (NetworkTypeIcon != null)
|
||||||
|
{
|
||||||
|
NetworkTypeIcon.FontSize = Math.Clamp(targetHeight * 0.35, 10, 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置最小和最大宽度
|
||||||
|
RootBorder.MinWidth = cellSize * 1.5;
|
||||||
|
RootBorder.MaxWidth = cellSize * 5;
|
||||||
|
|
||||||
|
if (_transparentBackground)
|
||||||
|
{
|
||||||
|
RootBorder.MinWidth = 0;
|
||||||
|
RootBorder.Padding = new Thickness(Math.Clamp(cellSize * 0.06, 4, 10), 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保清除可能存在的固定 Padding,由代码控制"紧密感"
|
||||||
|
RootBorder.Padding = new Thickness(Math.Clamp(cellSize * 0.15, 12, 24), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyChrome()
|
||||||
|
{
|
||||||
|
if (_transparentBackground)
|
||||||
|
{
|
||||||
|
RootBorder.Classes.Remove("glass-panel");
|
||||||
|
RootBorder.Background = Brushes.Transparent;
|
||||||
|
RootBorder.BorderBrush = Brushes.Transparent;
|
||||||
|
RootBorder.BorderThickness = new Thickness(0);
|
||||||
|
RootBorder.BoxShadow = default;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!RootBorder.Classes.Contains("glass-panel"))
|
||||||
|
{
|
||||||
|
RootBorder.Classes.Add("glass-panel");
|
||||||
|
}
|
||||||
|
|
||||||
|
RootBorder.ClearValue(Border.BackgroundProperty);
|
||||||
|
RootBorder.ClearValue(Border.BorderBrushProperty);
|
||||||
|
RootBorder.ClearValue(Border.BorderThicknessProperty);
|
||||||
|
RootBorder.ClearValue(Border.BoxShadowProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CornerRadius ResolveUnifiedMainRectangle() => new(ResolveUnifiedMainRadiusValue());
|
||||||
|
|
||||||
|
private static double ResolveUnifiedMainRadiusValue() =>
|
||||||
|
HostAppearanceThemeProvider.GetOrCreate().GetCurrent().CornerRadiusTokens.Lg.TopLeft;
|
||||||
|
|
||||||
|
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnDetachedFromVisualTree(e);
|
||||||
|
_timer?.Stop();
|
||||||
|
_networkTypeTimer?.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -365,14 +365,29 @@ public partial class MainWindow
|
|||||||
: ClockDisplayFormat.HourMinuteSecond;
|
: ClockDisplayFormat.HourMinuteSecond;
|
||||||
_statusBarClockTransparentBackground = snapshot.StatusBarClockTransparentBackground;
|
_statusBarClockTransparentBackground = snapshot.StatusBarClockTransparentBackground;
|
||||||
_clockPosition = NormalizeClockPosition(snapshot.ClockPosition);
|
_clockPosition = NormalizeClockPosition(snapshot.ClockPosition);
|
||||||
|
_clockFontSize = NormalizeFontSize(snapshot.ClockFontSize);
|
||||||
|
|
||||||
_showTextCapsule = snapshot.ShowTextCapsule;
|
_showTextCapsule = snapshot.ShowTextCapsule;
|
||||||
_textCapsuleContent = snapshot.TextCapsuleContent ?? "**Hello** World!";
|
_textCapsuleContent = snapshot.TextCapsuleContent ?? "**Hello** World!";
|
||||||
_textCapsulePosition = NormalizeTextCapsulePosition(snapshot.TextCapsulePosition);
|
_textCapsulePosition = NormalizeTextCapsulePosition(snapshot.TextCapsulePosition);
|
||||||
_textCapsuleTransparentBackground = snapshot.TextCapsuleTransparentBackground;
|
_textCapsuleTransparentBackground = snapshot.TextCapsuleTransparentBackground;
|
||||||
|
_textCapsuleFontSize = NormalizeFontSize(snapshot.TextCapsuleFontSize);
|
||||||
|
|
||||||
|
_showNetworkSpeed = snapshot.ShowNetworkSpeed;
|
||||||
|
_networkSpeedPosition = NormalizeNetworkSpeedPosition(snapshot.NetworkSpeedPosition);
|
||||||
|
_networkSpeedDisplayMode = NormalizeNetworkSpeedDisplayMode(snapshot.NetworkSpeedDisplayMode);
|
||||||
|
_networkSpeedTransparentBackground = snapshot.NetworkSpeedTransparentBackground;
|
||||||
|
_showNetworkTypeIcon = snapshot.ShowNetworkTypeIcon;
|
||||||
|
_networkSpeedFontSize = NormalizeFontSize(snapshot.NetworkSpeedFontSize);
|
||||||
|
|
||||||
|
_statusBarShadowEnabled = snapshot.StatusBarShadowEnabled;
|
||||||
|
_statusBarShadowColor = snapshot.StatusBarShadowColor ?? "#000000";
|
||||||
|
_statusBarShadowOpacity = snapshot.StatusBarShadowOpacity;
|
||||||
|
|
||||||
ApplyClockSettingsToAllWidgets();
|
ApplyClockSettingsToAllWidgets();
|
||||||
ApplyTextCapsuleSettingsToAllWidgets();
|
ApplyTextCapsuleSettingsToAllWidgets();
|
||||||
|
ApplyNetworkSpeedSettingsToAllWidgets();
|
||||||
|
ApplyStatusBarShadow();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyClockSettingsToAllWidgets()
|
private void ApplyClockSettingsToAllWidgets()
|
||||||
@@ -381,16 +396,19 @@ public partial class MainWindow
|
|||||||
{
|
{
|
||||||
ClockWidgetLeft.SetDisplayFormat(_clockDisplayFormat);
|
ClockWidgetLeft.SetDisplayFormat(_clockDisplayFormat);
|
||||||
ClockWidgetLeft.SetTransparentBackground(_statusBarClockTransparentBackground);
|
ClockWidgetLeft.SetTransparentBackground(_statusBarClockTransparentBackground);
|
||||||
|
ClockWidgetLeft.SetFontSize(_clockFontSize);
|
||||||
}
|
}
|
||||||
if (ClockWidgetCenter is not null)
|
if (ClockWidgetCenter is not null)
|
||||||
{
|
{
|
||||||
ClockWidgetCenter.SetDisplayFormat(_clockDisplayFormat);
|
ClockWidgetCenter.SetDisplayFormat(_clockDisplayFormat);
|
||||||
ClockWidgetCenter.SetTransparentBackground(_statusBarClockTransparentBackground);
|
ClockWidgetCenter.SetTransparentBackground(_statusBarClockTransparentBackground);
|
||||||
|
ClockWidgetCenter.SetFontSize(_clockFontSize);
|
||||||
}
|
}
|
||||||
if (ClockWidgetRight is not null)
|
if (ClockWidgetRight is not null)
|
||||||
{
|
{
|
||||||
ClockWidgetRight.SetDisplayFormat(_clockDisplayFormat);
|
ClockWidgetRight.SetDisplayFormat(_clockDisplayFormat);
|
||||||
ClockWidgetRight.SetTransparentBackground(_statusBarClockTransparentBackground);
|
ClockWidgetRight.SetTransparentBackground(_statusBarClockTransparentBackground);
|
||||||
|
ClockWidgetRight.SetFontSize(_clockFontSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,6 +422,16 @@ public partial class MainWindow
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string NormalizeFontSize(string? value)
|
||||||
|
{
|
||||||
|
return value switch
|
||||||
|
{
|
||||||
|
_ when string.Equals(value, "Small", StringComparison.OrdinalIgnoreCase) => "Small",
|
||||||
|
_ when string.Equals(value, "Large", StringComparison.OrdinalIgnoreCase) => "Large",
|
||||||
|
_ => "Medium"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private void ApplyTextCapsuleSettingsToAllWidgets()
|
private void ApplyTextCapsuleSettingsToAllWidgets()
|
||||||
{
|
{
|
||||||
if (TextCapsuleWidgetLeft is not null)
|
if (TextCapsuleWidgetLeft is not null)
|
||||||
@@ -433,6 +461,90 @@ public partial class MainWindow
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ApplyNetworkSpeedSettingsToAllWidgets()
|
||||||
|
{
|
||||||
|
if (NetworkSpeedWidgetLeft is not null)
|
||||||
|
{
|
||||||
|
NetworkSpeedWidgetLeft.SetDisplayMode(_networkSpeedDisplayMode);
|
||||||
|
NetworkSpeedWidgetLeft.SetTransparentBackground(_networkSpeedTransparentBackground);
|
||||||
|
NetworkSpeedWidgetLeft.SetShowNetworkTypeIcon(_showNetworkTypeIcon);
|
||||||
|
NetworkSpeedWidgetLeft.SetFontSize(_networkSpeedFontSize);
|
||||||
|
}
|
||||||
|
if (NetworkSpeedWidgetCenter is not null)
|
||||||
|
{
|
||||||
|
NetworkSpeedWidgetCenter.SetDisplayMode(_networkSpeedDisplayMode);
|
||||||
|
NetworkSpeedWidgetCenter.SetTransparentBackground(_networkSpeedTransparentBackground);
|
||||||
|
NetworkSpeedWidgetCenter.SetShowNetworkTypeIcon(_showNetworkTypeIcon);
|
||||||
|
NetworkSpeedWidgetCenter.SetFontSize(_networkSpeedFontSize);
|
||||||
|
}
|
||||||
|
if (NetworkSpeedWidgetRight is not null)
|
||||||
|
{
|
||||||
|
NetworkSpeedWidgetRight.SetDisplayMode(_networkSpeedDisplayMode);
|
||||||
|
NetworkSpeedWidgetRight.SetTransparentBackground(_networkSpeedTransparentBackground);
|
||||||
|
NetworkSpeedWidgetRight.SetShowNetworkTypeIcon(_showNetworkTypeIcon);
|
||||||
|
NetworkSpeedWidgetRight.SetFontSize(_networkSpeedFontSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeNetworkSpeedPosition(string? value)
|
||||||
|
{
|
||||||
|
return value switch
|
||||||
|
{
|
||||||
|
_ when string.Equals(value, "Left", StringComparison.OrdinalIgnoreCase) => "Left",
|
||||||
|
_ when string.Equals(value, "Center", StringComparison.OrdinalIgnoreCase) => "Center",
|
||||||
|
_ => "Right"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeNetworkSpeedDisplayMode(string? value)
|
||||||
|
{
|
||||||
|
return value switch
|
||||||
|
{
|
||||||
|
_ when string.Equals(value, "Upload", StringComparison.OrdinalIgnoreCase) => "Upload",
|
||||||
|
_ when string.Equals(value, "Download", StringComparison.OrdinalIgnoreCase) => "Download",
|
||||||
|
_ => "Both"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyStatusBarShadow()
|
||||||
|
{
|
||||||
|
if (StatusBarOverlay is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_statusBarShadowEnabled)
|
||||||
|
{
|
||||||
|
if (Color.TryParse(_statusBarShadowColor, out var shadowColor))
|
||||||
|
{
|
||||||
|
var opacity = Math.Clamp(_statusBarShadowOpacity, 0, 1);
|
||||||
|
|
||||||
|
StatusBarOverlay.IsVisible = true;
|
||||||
|
|
||||||
|
var gradientBrush = new LinearGradientBrush
|
||||||
|
{
|
||||||
|
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
|
||||||
|
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative)
|
||||||
|
};
|
||||||
|
|
||||||
|
var alpha1 = (byte)(shadowColor.A * opacity * 0.8);
|
||||||
|
var alpha2 = (byte)(shadowColor.A * opacity * 0.4);
|
||||||
|
var color1 = Color.FromArgb(alpha1, shadowColor.R, shadowColor.G, shadowColor.B);
|
||||||
|
var color2 = Color.FromArgb(alpha2, shadowColor.R, shadowColor.G, shadowColor.B);
|
||||||
|
|
||||||
|
gradientBrush.GradientStops.Add(new GradientStop(color1, 0.0));
|
||||||
|
gradientBrush.GradientStops.Add(new GradientStop(color2, 0.3));
|
||||||
|
gradientBrush.GradientStops.Add(new GradientStop(Colors.Transparent, 1.0));
|
||||||
|
|
||||||
|
StatusBarOverlay.Background = gradientBrush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StatusBarOverlay.IsVisible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 检测状态栏组件是否会发生碰撞
|
/// 检测状态栏组件是否会发生碰撞
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -676,6 +788,14 @@ public partial class MainWindow
|
|||||||
if (TextCapsuleWidgetRight is not null)
|
if (TextCapsuleWidgetRight is not null)
|
||||||
TextCapsuleWidgetRight.IsVisible = false;
|
TextCapsuleWidgetRight.IsVisible = false;
|
||||||
|
|
||||||
|
// 先隐藏所有网速控件
|
||||||
|
if (NetworkSpeedWidgetLeft is not null)
|
||||||
|
NetworkSpeedWidgetLeft.IsVisible = false;
|
||||||
|
if (NetworkSpeedWidgetCenter is not null)
|
||||||
|
NetworkSpeedWidgetCenter.IsVisible = false;
|
||||||
|
if (NetworkSpeedWidgetRight is not null)
|
||||||
|
NetworkSpeedWidgetRight.IsVisible = false;
|
||||||
|
|
||||||
// 根据位置设置显示对应的时钟控件(带碰撞检测)
|
// 根据位置设置显示对应的时钟控件(带碰撞检测)
|
||||||
if (showClock)
|
if (showClock)
|
||||||
{
|
{
|
||||||
@@ -770,6 +890,53 @@ public partial class MainWindow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 根据位置设置显示对应的网速控件(带碰撞检测)
|
||||||
|
if (_showNetworkSpeed)
|
||||||
|
{
|
||||||
|
var targetPosition = _networkSpeedPosition;
|
||||||
|
var canAdd = CanAddComponentAtPosition(targetPosition);
|
||||||
|
|
||||||
|
if (canAdd)
|
||||||
|
{
|
||||||
|
var targetNetworkSpeed = targetPosition switch
|
||||||
|
{
|
||||||
|
"Left" => NetworkSpeedWidgetLeft,
|
||||||
|
"Center" => NetworkSpeedWidgetCenter,
|
||||||
|
_ => NetworkSpeedWidgetRight
|
||||||
|
};
|
||||||
|
|
||||||
|
if (targetNetworkSpeed is not null)
|
||||||
|
{
|
||||||
|
targetNetworkSpeed.IsVisible = true;
|
||||||
|
targetNetworkSpeed.SetTransparentBackground(_networkSpeedTransparentBackground);
|
||||||
|
targetNetworkSpeed.SetDisplayMode(_networkSpeedDisplayMode);
|
||||||
|
hasVisibleTopStatusComponent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 如果目标位置无法添加,尝试其他位置
|
||||||
|
var alternativePosition = FindAlternativePosition(targetPosition);
|
||||||
|
if (alternativePosition is not null)
|
||||||
|
{
|
||||||
|
var targetNetworkSpeed = alternativePosition switch
|
||||||
|
{
|
||||||
|
"Left" => NetworkSpeedWidgetLeft,
|
||||||
|
"Center" => NetworkSpeedWidgetCenter,
|
||||||
|
_ => NetworkSpeedWidgetRight
|
||||||
|
};
|
||||||
|
|
||||||
|
if (targetNetworkSpeed is not null)
|
||||||
|
{
|
||||||
|
targetNetworkSpeed.IsVisible = true;
|
||||||
|
targetNetworkSpeed.SetTransparentBackground(_networkSpeedTransparentBackground);
|
||||||
|
targetNetworkSpeed.SetDisplayMode(_networkSpeedDisplayMode);
|
||||||
|
hasVisibleTopStatusComponent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (TopStatusBarHost is not null)
|
if (TopStatusBarHost is not null)
|
||||||
{
|
{
|
||||||
TopStatusBarHost.IsVisible = hasVisibleTopStatusComponent;
|
TopStatusBarHost.IsVisible = hasVisibleTopStatusComponent;
|
||||||
@@ -871,6 +1038,82 @@ public partial class MainWindow
|
|||||||
TextCapsuleWidgetCenter.IsVisible = false;
|
TextCapsuleWidgetCenter.IsVisible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 调整网速组件位置(优先级:时钟 > 文字胶囊 > 网速)
|
||||||
|
if (NetworkSpeedWidgetLeft?.IsVisible == true && WouldComponentsCollide())
|
||||||
|
{
|
||||||
|
// 尝试将左侧网速移到中间
|
||||||
|
if (CanAddComponentAtPosition("Center"))
|
||||||
|
{
|
||||||
|
NetworkSpeedWidgetLeft.IsVisible = false;
|
||||||
|
NetworkSpeedWidgetCenter!.IsVisible = true;
|
||||||
|
NetworkSpeedWidgetCenter.SetTransparentBackground(_networkSpeedTransparentBackground);
|
||||||
|
NetworkSpeedWidgetCenter.SetDisplayMode(_networkSpeedDisplayMode);
|
||||||
|
}
|
||||||
|
// 或者移到右侧
|
||||||
|
else if (CanAddComponentAtPosition("Right"))
|
||||||
|
{
|
||||||
|
NetworkSpeedWidgetLeft.IsVisible = false;
|
||||||
|
NetworkSpeedWidgetRight!.IsVisible = true;
|
||||||
|
NetworkSpeedWidgetRight.SetTransparentBackground(_networkSpeedTransparentBackground);
|
||||||
|
NetworkSpeedWidgetRight.SetDisplayMode(_networkSpeedDisplayMode);
|
||||||
|
}
|
||||||
|
// 如果都无法添加,则隐藏网速
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NetworkSpeedWidgetLeft.IsVisible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NetworkSpeedWidgetRight?.IsVisible == true && WouldComponentsCollide())
|
||||||
|
{
|
||||||
|
// 尝试将右侧网速移到中间
|
||||||
|
if (CanAddComponentAtPosition("Center"))
|
||||||
|
{
|
||||||
|
NetworkSpeedWidgetRight.IsVisible = false;
|
||||||
|
NetworkSpeedWidgetCenter!.IsVisible = true;
|
||||||
|
NetworkSpeedWidgetCenter.SetTransparentBackground(_networkSpeedTransparentBackground);
|
||||||
|
NetworkSpeedWidgetCenter.SetDisplayMode(_networkSpeedDisplayMode);
|
||||||
|
}
|
||||||
|
// 或者移到左侧
|
||||||
|
else if (CanAddComponentAtPosition("Left"))
|
||||||
|
{
|
||||||
|
NetworkSpeedWidgetRight.IsVisible = false;
|
||||||
|
NetworkSpeedWidgetLeft!.IsVisible = true;
|
||||||
|
NetworkSpeedWidgetLeft.SetTransparentBackground(_networkSpeedTransparentBackground);
|
||||||
|
NetworkSpeedWidgetLeft.SetDisplayMode(_networkSpeedDisplayMode);
|
||||||
|
}
|
||||||
|
// 如果都无法添加,则隐藏网速
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NetworkSpeedWidgetRight.IsVisible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NetworkSpeedWidgetCenter?.IsVisible == true && WouldComponentsCollide())
|
||||||
|
{
|
||||||
|
// 尝试将中间网速移到左侧
|
||||||
|
if (CanAddComponentAtPosition("Left"))
|
||||||
|
{
|
||||||
|
NetworkSpeedWidgetCenter.IsVisible = false;
|
||||||
|
NetworkSpeedWidgetLeft!.IsVisible = true;
|
||||||
|
NetworkSpeedWidgetLeft.SetTransparentBackground(_networkSpeedTransparentBackground);
|
||||||
|
NetworkSpeedWidgetLeft.SetDisplayMode(_networkSpeedDisplayMode);
|
||||||
|
}
|
||||||
|
// 或者移到右侧
|
||||||
|
else if (CanAddComponentAtPosition("Right"))
|
||||||
|
{
|
||||||
|
NetworkSpeedWidgetCenter.IsVisible = false;
|
||||||
|
NetworkSpeedWidgetRight!.IsVisible = true;
|
||||||
|
NetworkSpeedWidgetRight.SetTransparentBackground(_networkSpeedTransparentBackground);
|
||||||
|
NetworkSpeedWidgetRight.SetDisplayMode(_networkSpeedDisplayMode);
|
||||||
|
}
|
||||||
|
// 如果都无法添加,则隐藏网速
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NetworkSpeedWidgetCenter.IsVisible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -650,8 +650,24 @@ public partial class MainWindow
|
|||||||
TaskbarLayoutMode = _taskbarLayoutMode,
|
TaskbarLayoutMode = _taskbarLayoutMode,
|
||||||
ClockDisplayFormat = _clockDisplayFormat == ClockDisplayFormat.HourMinute ? "HourMinute" : "HourMinuteSecond",
|
ClockDisplayFormat = _clockDisplayFormat == ClockDisplayFormat.HourMinute ? "HourMinute" : "HourMinuteSecond",
|
||||||
StatusBarClockTransparentBackground = _statusBarClockTransparentBackground,
|
StatusBarClockTransparentBackground = _statusBarClockTransparentBackground,
|
||||||
|
ClockPosition = _clockPosition,
|
||||||
|
ClockFontSize = _clockFontSize,
|
||||||
|
ShowTextCapsule = _showTextCapsule,
|
||||||
|
TextCapsuleContent = _textCapsuleContent,
|
||||||
|
TextCapsulePosition = _textCapsulePosition,
|
||||||
|
TextCapsuleTransparentBackground = _textCapsuleTransparentBackground,
|
||||||
|
TextCapsuleFontSize = _textCapsuleFontSize,
|
||||||
|
ShowNetworkSpeed = _showNetworkSpeed,
|
||||||
|
NetworkSpeedPosition = _networkSpeedPosition,
|
||||||
|
NetworkSpeedDisplayMode = _networkSpeedDisplayMode,
|
||||||
|
NetworkSpeedTransparentBackground = _networkSpeedTransparentBackground,
|
||||||
|
ShowNetworkTypeIcon = _showNetworkTypeIcon,
|
||||||
|
NetworkSpeedFontSize = _networkSpeedFontSize,
|
||||||
StatusBarSpacingMode = _statusBarSpacingMode,
|
StatusBarSpacingMode = _statusBarSpacingMode,
|
||||||
StatusBarCustomSpacingPercent = _statusBarCustomSpacingPercent,
|
StatusBarCustomSpacingPercent = _statusBarCustomSpacingPercent,
|
||||||
|
StatusBarShadowEnabled = _statusBarShadowEnabled,
|
||||||
|
StatusBarShadowColor = _statusBarShadowColor,
|
||||||
|
StatusBarShadowOpacity = _statusBarShadowOpacity,
|
||||||
DisabledPluginIds = existingSnapshot.DisabledPluginIds,
|
DisabledPluginIds = existingSnapshot.DisabledPluginIds,
|
||||||
StudyFrameMs = existingSnapshot.StudyFrameMs,
|
StudyFrameMs = existingSnapshot.StudyFrameMs,
|
||||||
StudyScoreThresholdDbfs = existingSnapshot.StudyScoreThresholdDbfs,
|
StudyScoreThresholdDbfs = existingSnapshot.StudyScoreThresholdDbfs,
|
||||||
|
|||||||
@@ -226,13 +226,34 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
|
<!-- 状态栏阴影层 - macOS 风格的完整阴影带 -->
|
||||||
|
<Border x:Name="StatusBarOverlay"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="1"
|
||||||
|
IsVisible="False"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
Height="24"
|
||||||
|
ZIndex="0"
|
||||||
|
Margin="0,0,0,-24">
|
||||||
|
<Border.Background>
|
||||||
|
<LinearGradientBrush StartPoint="0%,0%" EndPoint="0%,100%">
|
||||||
|
<GradientStop Color="#CC000000" Offset="0.0" />
|
||||||
|
<GradientStop Color="#66000000" Offset="0.3" />
|
||||||
|
<GradientStop Color="#00000000" Offset="1.0" />
|
||||||
|
</LinearGradientBrush>
|
||||||
|
</Border.Background>
|
||||||
|
</Border>
|
||||||
|
|
||||||
<Border x:Name="TopStatusBarHost"
|
<Border x:Name="TopStatusBarHost"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.ColumnSpan="1"
|
Grid.ColumnSpan="1"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
BorderThickness="0"
|
BorderThickness="0"
|
||||||
Padding="4">
|
Padding="4"
|
||||||
|
ZIndex="2">
|
||||||
<Grid ColumnDefinitions="*,Auto,*">
|
<Grid ColumnDefinitions="*,Auto,*">
|
||||||
<!-- 左侧状态栏组件 -->
|
<!-- 左侧状态栏组件 -->
|
||||||
<StackPanel x:Name="TopStatusLeftPanel"
|
<StackPanel x:Name="TopStatusLeftPanel"
|
||||||
@@ -246,6 +267,9 @@
|
|||||||
<comp:TextCapsuleWidget x:Name="TextCapsuleWidgetLeft"
|
<comp:TextCapsuleWidget x:Name="TextCapsuleWidgetLeft"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
Margin="0" />
|
Margin="0" />
|
||||||
|
<comp:NetworkSpeedWidget x:Name="NetworkSpeedWidgetLeft"
|
||||||
|
IsVisible="False"
|
||||||
|
Margin="0" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<!-- 中间状态栏组件 -->
|
<!-- 中间状态栏组件 -->
|
||||||
<StackPanel x:Name="TopStatusCenterPanel"
|
<StackPanel x:Name="TopStatusCenterPanel"
|
||||||
@@ -259,6 +283,9 @@
|
|||||||
<comp:TextCapsuleWidget x:Name="TextCapsuleWidgetCenter"
|
<comp:TextCapsuleWidget x:Name="TextCapsuleWidgetCenter"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
Margin="0" />
|
Margin="0" />
|
||||||
|
<comp:NetworkSpeedWidget x:Name="NetworkSpeedWidgetCenter"
|
||||||
|
IsVisible="False"
|
||||||
|
Margin="0" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<!-- 右侧状态栏组件 -->
|
<!-- 右侧状态栏组件 -->
|
||||||
<StackPanel x:Name="TopStatusRightPanel"
|
<StackPanel x:Name="TopStatusRightPanel"
|
||||||
@@ -272,6 +299,9 @@
|
|||||||
<comp:TextCapsuleWidget x:Name="TextCapsuleWidgetRight"
|
<comp:TextCapsuleWidget x:Name="TextCapsuleWidgetRight"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
Margin="0" />
|
Margin="0" />
|
||||||
|
<comp:NetworkSpeedWidget x:Name="NetworkSpeedWidgetRight"
|
||||||
|
IsVisible="False"
|
||||||
|
Margin="0" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@@ -136,10 +136,21 @@ public partial class MainWindow : Window, ISettingsWindowAnchorProvider
|
|||||||
private int _statusBarCustomSpacingPercent = 12;
|
private int _statusBarCustomSpacingPercent = 12;
|
||||||
private bool _statusBarClockTransparentBackground;
|
private bool _statusBarClockTransparentBackground;
|
||||||
private string _clockPosition = "Left"; // Left, Center, Right
|
private string _clockPosition = "Left"; // Left, Center, Right
|
||||||
|
private string _clockFontSize = "Medium"; // Small, Medium, Large
|
||||||
private bool _showTextCapsule;
|
private bool _showTextCapsule;
|
||||||
private string _textCapsuleContent = "**Hello** World!";
|
private string _textCapsuleContent = "**Hello** World!";
|
||||||
private string _textCapsulePosition = "Right"; // Left, Center, Right
|
private string _textCapsulePosition = "Right"; // Left, Center, Right
|
||||||
private bool _textCapsuleTransparentBackground;
|
private bool _textCapsuleTransparentBackground;
|
||||||
|
private string _textCapsuleFontSize = "Medium"; // Small, Medium, Large
|
||||||
|
private bool _showNetworkSpeed;
|
||||||
|
private string _networkSpeedPosition = "Right"; // Left, Center, Right
|
||||||
|
private string _networkSpeedDisplayMode = "Both"; // Upload, Download, Both
|
||||||
|
private bool _networkSpeedTransparentBackground;
|
||||||
|
private bool _showNetworkTypeIcon;
|
||||||
|
private string _networkSpeedFontSize = "Medium"; // Small, Medium, Large
|
||||||
|
private bool _statusBarShadowEnabled;
|
||||||
|
private string _statusBarShadowColor = "#000000";
|
||||||
|
private double _statusBarShadowOpacity = 0.3;
|
||||||
private int _desktopEdgeInsetPercent = DefaultEdgeInsetPercent;
|
private int _desktopEdgeInsetPercent = DefaultEdgeInsetPercent;
|
||||||
private string _taskbarLayoutMode = TaskbarLayoutBottomFullRowMacStyle;
|
private string _taskbarLayoutMode = TaskbarLayoutBottomFullRowMacStyle;
|
||||||
private string _languageCode = "zh-CN";
|
private string _languageCode = "zh-CN";
|
||||||
@@ -720,6 +731,13 @@ public partial class MainWindow : Window, ISettingsWindowAnchorProvider
|
|||||||
TextCapsuleWidgetRight.Margin = new Thickness(0);
|
TextCapsuleWidgetRight.Margin = new Thickness(0);
|
||||||
TextCapsuleWidgetRight.ApplyCellSize(cellSize);
|
TextCapsuleWidgetRight.ApplyCellSize(cellSize);
|
||||||
|
|
||||||
|
NetworkSpeedWidgetLeft.Margin = new Thickness(0);
|
||||||
|
NetworkSpeedWidgetLeft.ApplyCellSize(cellSize);
|
||||||
|
NetworkSpeedWidgetCenter.Margin = new Thickness(0);
|
||||||
|
NetworkSpeedWidgetCenter.ApplyCellSize(cellSize);
|
||||||
|
NetworkSpeedWidgetRight.Margin = new Thickness(0);
|
||||||
|
NetworkSpeedWidgetRight.ApplyCellSize(cellSize);
|
||||||
|
|
||||||
var buttonMinWidth = Math.Clamp(taskbarCellHeight * 2.35, 100, 340);
|
var buttonMinWidth = Math.Clamp(taskbarCellHeight * 2.35, 100, 340);
|
||||||
|
|
||||||
BackToWindowsButton.Margin = new Thickness(0);
|
BackToWindowsButton.Margin = new Thickness(0);
|
||||||
@@ -763,6 +781,9 @@ public partial class MainWindow : Window, ISettingsWindowAnchorProvider
|
|||||||
TextCapsuleWidgetLeft.ApplyCellSize(_currentDesktopCellSize);
|
TextCapsuleWidgetLeft.ApplyCellSize(_currentDesktopCellSize);
|
||||||
TextCapsuleWidgetCenter.ApplyCellSize(_currentDesktopCellSize);
|
TextCapsuleWidgetCenter.ApplyCellSize(_currentDesktopCellSize);
|
||||||
TextCapsuleWidgetRight.ApplyCellSize(_currentDesktopCellSize);
|
TextCapsuleWidgetRight.ApplyCellSize(_currentDesktopCellSize);
|
||||||
|
NetworkSpeedWidgetLeft.ApplyCellSize(_currentDesktopCellSize);
|
||||||
|
NetworkSpeedWidgetCenter.ApplyCellSize(_currentDesktopCellSize);
|
||||||
|
NetworkSpeedWidgetRight.ApplyCellSize(_currentDesktopCellSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,24 @@
|
|||||||
</ComboBox>
|
</ComboBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
</ui:SettingsExpanderItem>
|
</ui:SettingsExpanderItem>
|
||||||
|
<ui:SettingsExpanderItem>
|
||||||
|
<Grid ColumnDefinitions="Auto,*"
|
||||||
|
ColumnSpacing="16">
|
||||||
|
<TextBlock Text="{Binding ClockFontSizeLabel}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<ComboBox Grid.Column="1"
|
||||||
|
Width="220"
|
||||||
|
IsEnabled="{Binding ShowClock}"
|
||||||
|
ItemsSource="{Binding ClockFontSizes}"
|
||||||
|
SelectedItem="{Binding SelectedClockFontSize}">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:SelectionOption">
|
||||||
|
<TextBlock Text="{Binding Label}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
</Grid>
|
||||||
|
</ui:SettingsExpanderItem>
|
||||||
</ui:SettingsExpander>
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
<ui:SettingsExpander Header="{Binding TextCapsuleHeader}"
|
<ui:SettingsExpander Header="{Binding TextCapsuleHeader}"
|
||||||
@@ -126,6 +144,92 @@
|
|||||||
</ui:SettingsExpanderItem>
|
</ui:SettingsExpanderItem>
|
||||||
</ui:SettingsExpander>
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
|
<ui:SettingsExpander Header="{Binding NetworkSpeedHeader}"
|
||||||
|
Description="{Binding NetworkSpeedDescription}">
|
||||||
|
<ui:SettingsExpander.IconSource>
|
||||||
|
<fi:SymbolIconSource Symbol="ArrowBidirectionalUpDown" />
|
||||||
|
</ui:SettingsExpander.IconSource>
|
||||||
|
<ui:SettingsExpander.Footer>
|
||||||
|
<ToggleSwitch IsChecked="{Binding ShowNetworkSpeed}" />
|
||||||
|
</ui:SettingsExpander.Footer>
|
||||||
|
<ui:SettingsExpanderItem>
|
||||||
|
<Grid ColumnDefinitions="Auto,*"
|
||||||
|
ColumnSpacing="16">
|
||||||
|
<TextBlock Text="{Binding NetworkSpeedPositionLabel}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<ComboBox Grid.Column="1"
|
||||||
|
Width="220"
|
||||||
|
IsEnabled="{Binding ShowNetworkSpeed}"
|
||||||
|
ItemsSource="{Binding NetworkSpeedPositions}"
|
||||||
|
SelectedItem="{Binding SelectedNetworkSpeedPosition}">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:SelectionOption">
|
||||||
|
<TextBlock Text="{Binding Label}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
</Grid>
|
||||||
|
</ui:SettingsExpanderItem>
|
||||||
|
<ui:SettingsExpanderItem>
|
||||||
|
<Grid ColumnDefinitions="Auto,*"
|
||||||
|
ColumnSpacing="16">
|
||||||
|
<TextBlock Text="{Binding NetworkSpeedDisplayModeLabel}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<ComboBox Grid.Column="1"
|
||||||
|
Width="220"
|
||||||
|
IsEnabled="{Binding ShowNetworkSpeed}"
|
||||||
|
ItemsSource="{Binding NetworkSpeedDisplayModes}"
|
||||||
|
SelectedItem="{Binding SelectedNetworkSpeedDisplayMode}">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:SelectionOption">
|
||||||
|
<TextBlock Text="{Binding Label}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
</Grid>
|
||||||
|
</ui:SettingsExpanderItem>
|
||||||
|
<ui:SettingsExpanderItem>
|
||||||
|
<Grid ColumnDefinitions="*,Auto"
|
||||||
|
ColumnSpacing="16">
|
||||||
|
<TextBlock Text="{Binding NetworkSpeedTransparentBackgroundLabel}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<ToggleSwitch Grid.Column="1"
|
||||||
|
IsChecked="{Binding NetworkSpeedTransparentBackground}"
|
||||||
|
IsEnabled="{Binding ShowNetworkSpeed}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Grid>
|
||||||
|
</ui:SettingsExpanderItem>
|
||||||
|
<ui:SettingsExpanderItem>
|
||||||
|
<Grid ColumnDefinitions="*,Auto"
|
||||||
|
ColumnSpacing="16">
|
||||||
|
<TextBlock Text="{Binding ShowNetworkTypeIconLabel}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<ToggleSwitch Grid.Column="1"
|
||||||
|
IsChecked="{Binding ShowNetworkTypeIcon}"
|
||||||
|
IsEnabled="{Binding ShowNetworkSpeed}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Grid>
|
||||||
|
</ui:SettingsExpanderItem>
|
||||||
|
<ui:SettingsExpanderItem>
|
||||||
|
<Grid ColumnDefinitions="Auto,*"
|
||||||
|
ColumnSpacing="16">
|
||||||
|
<TextBlock Text="{Binding NetworkSpeedFontSizeLabel}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<ComboBox Grid.Column="1"
|
||||||
|
Width="220"
|
||||||
|
IsEnabled="{Binding ShowNetworkSpeed}"
|
||||||
|
ItemsSource="{Binding NetworkSpeedFontSizes}"
|
||||||
|
SelectedItem="{Binding SelectedNetworkSpeedFontSize}">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:SelectionOption">
|
||||||
|
<TextBlock Text="{Binding Label}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
</Grid>
|
||||||
|
</ui:SettingsExpanderItem>
|
||||||
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
<Separator Classes="settings-separator" />
|
<Separator Classes="settings-separator" />
|
||||||
|
|
||||||
<controls:IconText Icon="Apps"
|
<controls:IconText Icon="Apps"
|
||||||
@@ -164,6 +268,55 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</ui:SettingsExpanderItem>
|
</ui:SettingsExpanderItem>
|
||||||
</ui:SettingsExpander>
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
|
<Separator Classes="settings-separator" />
|
||||||
|
|
||||||
|
<controls:IconText Icon="Square"
|
||||||
|
Text="{Binding StatusBarShadowHeader}"
|
||||||
|
Margin="0,0,0,4" />
|
||||||
|
|
||||||
|
<ui:SettingsExpander Header="{Binding StatusBarShadowHeader}"
|
||||||
|
Description="{Binding StatusBarShadowDescription}">
|
||||||
|
<ui:SettingsExpander.IconSource>
|
||||||
|
<fi:SymbolIconSource Symbol="Square" />
|
||||||
|
</ui:SettingsExpander.IconSource>
|
||||||
|
<ui:SettingsExpander.Footer>
|
||||||
|
<ToggleSwitch IsChecked="{Binding StatusBarShadowEnabled}" />
|
||||||
|
</ui:SettingsExpander.Footer>
|
||||||
|
<ui:SettingsExpanderItem>
|
||||||
|
<Grid ColumnDefinitions="Auto,*"
|
||||||
|
ColumnSpacing="16">
|
||||||
|
<TextBlock Text="{Binding StatusBarShadowColorLabel}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<Button Grid.Column="1"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
IsEnabled="{Binding StatusBarShadowEnabled}">
|
||||||
|
<Border Width="32"
|
||||||
|
Height="32"
|
||||||
|
CornerRadius="4"
|
||||||
|
Background="{Binding StatusBarShadowColorBrush}" />
|
||||||
|
<Button.Flyout>
|
||||||
|
<Flyout Placement="BottomEdgeAlignedRight">
|
||||||
|
<ColorPicker Color="{Binding StatusBarShadowColor}" />
|
||||||
|
</Flyout>
|
||||||
|
</Button.Flyout>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</ui:SettingsExpanderItem>
|
||||||
|
<ui:SettingsExpanderItem>
|
||||||
|
<Grid ColumnDefinitions="Auto,*"
|
||||||
|
ColumnSpacing="16">
|
||||||
|
<TextBlock Text="{Binding StatusBarShadowOpacityLabel}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<Slider Grid.Column="1"
|
||||||
|
Minimum="0"
|
||||||
|
Maximum="100"
|
||||||
|
TickFrequency="10"
|
||||||
|
IsEnabled="{Binding StatusBarShadowEnabled}"
|
||||||
|
Value="{Binding StatusBarShadowOpacity}" />
|
||||||
|
</Grid>
|
||||||
|
</ui:SettingsExpanderItem>
|
||||||
|
</ui:SettingsExpander>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
Reference in New Issue
Block a user