mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-07-01 15:44:26 +08:00
0.2.1
完善了日历组件
This commit is contained in:
@@ -5,4 +5,6 @@ public static class BuiltInComponentIds
|
|||||||
public const string Clock = "Clock";
|
public const string Clock = "Clock";
|
||||||
public const string Blank2x4 = "Blank2x4";
|
public const string Blank2x4 = "Blank2x4";
|
||||||
public const string Date = "Date";
|
public const string Date = "Date";
|
||||||
|
public const string MonthCalendar = "MonthCalendar";
|
||||||
|
public const string LunarCalendar = "LunarCalendar";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,18 +26,36 @@ public sealed class ComponentRegistry
|
|||||||
"Clock",
|
"Clock",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Status",
|
"Status",
|
||||||
MinWidthCells: 1,
|
MinWidthCells: 3,
|
||||||
MinHeightCells: 1,
|
MinHeightCells: 1,
|
||||||
AllowStatusBarPlacement: true,
|
AllowStatusBarPlacement: true,
|
||||||
AllowDesktopPlacement: false),
|
AllowDesktopPlacement: false),
|
||||||
new DesktopComponentDefinition(
|
new DesktopComponentDefinition(
|
||||||
BuiltInComponentIds.Date,
|
BuiltInComponentIds.Date,
|
||||||
"Date",
|
"Calendar",
|
||||||
"Calendar",
|
"Calendar",
|
||||||
"Date",
|
"Date",
|
||||||
MinWidthCells: 4,
|
MinWidthCells: 4,
|
||||||
MinHeightCells: 2,
|
MinHeightCells: 2,
|
||||||
AllowStatusBarPlacement: false,
|
AllowStatusBarPlacement: false,
|
||||||
|
AllowDesktopPlacement: true),
|
||||||
|
new DesktopComponentDefinition(
|
||||||
|
BuiltInComponentIds.MonthCalendar,
|
||||||
|
"Month Calendar",
|
||||||
|
"CalendarMonth",
|
||||||
|
"Date",
|
||||||
|
MinWidthCells: 2,
|
||||||
|
MinHeightCells: 2,
|
||||||
|
AllowStatusBarPlacement: false,
|
||||||
|
AllowDesktopPlacement: true),
|
||||||
|
new DesktopComponentDefinition(
|
||||||
|
BuiltInComponentIds.LunarCalendar,
|
||||||
|
"Lunar Calendar",
|
||||||
|
"Calendar",
|
||||||
|
"Date",
|
||||||
|
MinWidthCells: 2,
|
||||||
|
MinHeightCells: 2,
|
||||||
|
AllowStatusBarPlacement: false,
|
||||||
AllowDesktopPlacement: true)
|
AllowDesktopPlacement: true)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,100 +1,118 @@
|
|||||||
{
|
{
|
||||||
"app.title": "LanMontainDesktop",
|
"app.title": "LanMontainDesktop",
|
||||||
"button.back_to_windows": "Back to Windows",
|
"button.back_to_windows": "Back to Windows",
|
||||||
"tooltip.back_to_windows": "Back to Windows",
|
"tooltip.back_to_windows": "Back to Windows",
|
||||||
"tooltip.open_settings": "Settings",
|
"tooltip.open_settings": "Settings",
|
||||||
"settings.title": "Settings",
|
"settings.title": "Settings",
|
||||||
"settings.back_to_desktop": "Back to Desktop",
|
"settings.back_to_desktop": "Back to Desktop",
|
||||||
"settings.nav_header": "Settings",
|
"settings.nav_header": "Settings",
|
||||||
"settings.nav.wallpaper": "Wallpaper",
|
"settings.nav.wallpaper": "Wallpaper",
|
||||||
"settings.nav.grid": "Grid",
|
"settings.nav.grid": "Grid",
|
||||||
"settings.nav.color": "Color",
|
"settings.nav.color": "Color",
|
||||||
"settings.nav.status_bar": "Status Bar",
|
"settings.nav.status_bar": "Status Bar",
|
||||||
"settings.nav.region": "Region",
|
"settings.nav.region": "Region",
|
||||||
"settings.wallpaper.title": "Wallpaper",
|
"settings.wallpaper.title": "Wallpaper",
|
||||||
"settings.wallpaper.description": "Pick an image or video to apply as the app window wallpaper immediately.",
|
"settings.wallpaper.description": "Pick an image or video to apply as the app window wallpaper immediately.",
|
||||||
"settings.wallpaper.current_label": "Current Wallpaper",
|
"settings.wallpaper.current_label": "Current Wallpaper",
|
||||||
"settings.wallpaper.placement_label": "Placement",
|
"settings.wallpaper.placement_label": "Placement",
|
||||||
"settings.wallpaper.pick_button": "Browse Files",
|
"settings.wallpaper.placement_desc": "Adjust how the image fills the desktop.",
|
||||||
"settings.wallpaper.clear_button": "Reset to Solid Color",
|
"settings.wallpaper.pick_button": "Browse Files",
|
||||||
"settings.wallpaper.no_selection": "No wallpaper selected.",
|
"settings.wallpaper.clear_button": "Reset to Solid Color",
|
||||||
"settings.wallpaper.storage_unavailable": "Storage provider is unavailable.",
|
"settings.wallpaper.no_selection": "No wallpaper selected.",
|
||||||
"settings.wallpaper.import_failed": "Failed to import wallpaper file.",
|
"settings.wallpaper.storage_unavailable": "Storage provider is unavailable.",
|
||||||
"settings.wallpaper.image_applied": "Image wallpaper applied.",
|
"settings.wallpaper.import_failed": "Failed to import wallpaper file.",
|
||||||
"settings.wallpaper.video_applied": "Video wallpaper applied.",
|
"settings.wallpaper.image_applied": "Image wallpaper applied.",
|
||||||
"settings.wallpaper.unsupported_file": "Selected file type is not supported.",
|
"settings.wallpaper.video_applied": "Video wallpaper applied.",
|
||||||
"settings.wallpaper.apply_failed_format": "Failed to apply wallpaper: {0}",
|
"settings.wallpaper.unsupported_file": "Selected file type is not supported.",
|
||||||
"settings.wallpaper.mode_format": "Wallpaper mode: {0}.",
|
"settings.wallpaper.apply_failed_format": "Failed to apply wallpaper: {0}",
|
||||||
"settings.wallpaper.video_mode": "Video wallpaper uses automatic fill mode.",
|
"settings.wallpaper.mode_format": "Wallpaper mode: {0}.",
|
||||||
"settings.wallpaper.cleared": "Background reset to solid color.",
|
"settings.wallpaper.video_mode": "Video wallpaper uses automatic fill mode.",
|
||||||
"settings.wallpaper.default_status": "Current background uses solid color.",
|
"settings.wallpaper.cleared": "Background reset to solid color.",
|
||||||
"settings.wallpaper.saved_not_found": "Saved wallpaper file was not found. Using solid color background.",
|
"settings.wallpaper.default_status": "Current background uses solid color.",
|
||||||
"settings.wallpaper.restored": "Wallpaper restored from saved settings.",
|
"settings.wallpaper.saved_not_found": "Saved wallpaper file was not found. Using solid color background.",
|
||||||
"settings.wallpaper.video_restored": "Video wallpaper restored from saved settings.",
|
"settings.wallpaper.restored": "Wallpaper restored from saved settings.",
|
||||||
"settings.wallpaper.restore_failed": "Failed to restore saved wallpaper. Using solid color background.",
|
"settings.wallpaper.video_restored": "Video wallpaper restored from saved settings.",
|
||||||
"settings.wallpaper.video_not_found": "Video wallpaper file not found.",
|
"settings.wallpaper.restore_failed": "Failed to restore saved wallpaper. Using solid color background.",
|
||||||
"settings.wallpaper.video_player_unavailable": "Video player is unavailable.",
|
"settings.wallpaper.video_not_found": "Video wallpaper file not found.",
|
||||||
"settings.wallpaper.video_play_failed_format": "Failed to play video wallpaper: {0}",
|
"settings.wallpaper.video_player_unavailable": "Video player is unavailable.",
|
||||||
"settings.grid.title": "Grid Layout",
|
"settings.wallpaper.video_play_failed_format": "Failed to play video wallpaper: {0}",
|
||||||
"settings.grid.description": "Every component must occupy at least one cell (minimum 1x1).",
|
"settings.grid.title": "Grid Layout",
|
||||||
"settings.grid.short_side_label": "Short Side Cells",
|
"settings.grid.description": "Every component must occupy at least one cell (minimum 1x1).",
|
||||||
"settings.grid.apply_button": "Apply",
|
"settings.grid.short_side_label": "Short Side Cells",
|
||||||
"settings.grid.info_format": "Grid: {0} cols x {1} rows | cell {2:F1}px (1:1)",
|
"settings.grid.spacing_label": "Grid Spacing",
|
||||||
"settings.color.title": "Color",
|
"settings.grid.spacing_relaxed": "Relaxed (iOS)",
|
||||||
"settings.color.description": "Switch day/night mode and choose app accent colors.",
|
"settings.grid.spacing_compact": "Compact (Android)",
|
||||||
"settings.color.day_night_label": "Day/Night Mode",
|
"settings.grid.edge_inset_label": "Screen Inset",
|
||||||
"settings.color.day_night_on": "Night",
|
"settings.grid.edge_inset_px_format": "≈ {0:F1}px",
|
||||||
"settings.color.day_night_off": "Day",
|
"settings.grid.apply_button": "Apply",
|
||||||
"settings.color.recommended_label": "Recommended Colors",
|
"settings.grid.info_format": "Grid: {0} cols x {1} rows | cell {2:F1}px (1:1)",
|
||||||
"settings.color.system_monet_label": "System Monet Colors",
|
"settings.color.title": "Color",
|
||||||
"settings.color.refresh_button": "Refresh",
|
"settings.color.description": "Switch day/night mode and choose app accent colors.",
|
||||||
"settings.color.mode_night": "Night mode enabled",
|
"settings.color.day_night_label": "Day/Night Mode",
|
||||||
"settings.color.mode_day": "Day mode enabled",
|
"settings.color.day_night_on": "Night",
|
||||||
"settings.color.mode_status_format": "Theme mode: {0}.",
|
"settings.color.day_night_off": "Day",
|
||||||
"settings.color.monet_refreshed": "Monet colors refreshed.",
|
"settings.color.recommended_label": "Recommended Colors",
|
||||||
"settings.color.theme_ready_format": "Theme color ready: {0}.",
|
"settings.color.system_monet_label": "System Monet Colors",
|
||||||
"settings.color.theme_applied_format": "{0} color applied: {1}.",
|
"settings.color.refresh_button": "Refresh",
|
||||||
"settings.color.theme_updated_wallpaper": "Wallpaper updated. Monet colors refreshed.",
|
"settings.color.mode_night": "Night mode enabled",
|
||||||
"settings.color.theme_updated_video": "Video wallpaper updated. Theme colors refreshed.",
|
"settings.color.mode_day": "Day mode enabled",
|
||||||
"settings.color.theme_cleared_wallpaper": "Wallpaper cleared. Monet colors refreshed.",
|
"settings.color.mode_status_format": "Theme mode: {0}.",
|
||||||
"settings.status_bar.title": "Status Bar",
|
"settings.color.monet_refreshed": "Monet colors refreshed.",
|
||||||
"settings.status_bar.description": "Choose which components appear on the top status bar.",
|
"settings.color.theme_ready_format": "Theme color ready: {0}.",
|
||||||
"settings.status_bar.clock_header": "Clock Component",
|
"settings.color.theme_applied_format": "{0} color applied: {1}.",
|
||||||
"settings.status_bar.clock_description": "Display a clock on the top status bar.",
|
"settings.color.theme_updated_wallpaper": "Wallpaper updated. Monet colors refreshed.",
|
||||||
"settings.region.title": "Region",
|
"settings.color.theme_updated_video": "Video wallpaper updated. Theme colors refreshed.",
|
||||||
"settings.region.description": "Choose language and apply immediately to settings and key UI.",
|
"settings.color.theme_cleared_wallpaper": "Wallpaper cleared. Monet colors refreshed.",
|
||||||
"settings.region.language_header": "Language",
|
"settings.status_bar.title": "Status Bar",
|
||||||
"settings.region.language_label": "Language",
|
"settings.status_bar.description": "Choose which components appear on the top status bar.",
|
||||||
"settings.region.language_zh": "Chinese",
|
"settings.status_bar.clock_header": "Clock Component",
|
||||||
"settings.region.language_en": "English",
|
"settings.status_bar.clock_description": "Display a clock on the top status bar.",
|
||||||
"settings.region.applied_format": "Language switched to: {0}",
|
"settings.status_bar.spacing_header": "Component Spacing",
|
||||||
"settings.footer": "LanMontainDesktop Settings",
|
"settings.status_bar.spacing_desc": "Adjust spacing between status bar components.",
|
||||||
"filepicker.title": "Select wallpaper",
|
"settings.status_bar.spacing_mode_compact": "Compact",
|
||||||
"filepicker.image_files": "Image files",
|
"settings.status_bar.spacing_mode_relaxed": "Relaxed",
|
||||||
"filepicker.video_files": "Video files",
|
"settings.status_bar.spacing_mode_custom": "Custom",
|
||||||
"common.day": "Day",
|
"settings.status_bar.spacing_custom_label": "Custom spacing (%)",
|
||||||
"common.night": "Night",
|
"settings.status_bar.spacing_custom_px_format": "≈ {0:F1}px",
|
||||||
"common.back": "Back",
|
"settings.region.title": "Region",
|
||||||
"common.close": "Close",
|
"settings.region.description": "Choose language and apply immediately to settings and key UI.",
|
||||||
"common.recommended": "Recommended",
|
"settings.region.language_header": "Language",
|
||||||
"common.monet": "Monet",
|
"settings.region.language_label": "Language",
|
||||||
"desktop.page_index_format": "Desktop {0}",
|
"settings.region.language_zh": "Chinese",
|
||||||
"launcher.title": "App Launcher",
|
"settings.region.language_en": "English",
|
||||||
"launcher.subtitle": "Apps and folders from Windows Start Menu",
|
"settings.region.applied_format": "Language switched to: {0}",
|
||||||
"launcher.empty": "No Start Menu entries found.",
|
"settings.footer": "LanMontainDesktop Settings",
|
||||||
"launcher.empty_folder": "This folder is empty.",
|
"filepicker.title": "Select wallpaper",
|
||||||
"launcher.folder_items_format": "{0} apps",
|
"filepicker.image_files": "Image files",
|
||||||
"button.component_library": "Edit Desktop",
|
"filepicker.video_files": "Video files",
|
||||||
"tooltip.component_library": "Edit Desktop",
|
"common.day": "Day",
|
||||||
"component_library.title": "Edit Desktop",
|
"common.night": "Night",
|
||||||
"component_library.empty": "Swipe to pick a category, tap to open, then drag a widget onto the desktop.",
|
"common.back": "Back",
|
||||||
"component_library.drag_hint": "Drag to place",
|
"common.close": "Close",
|
||||||
"component_category.date": "Date",
|
"common.recommended": "Recommended",
|
||||||
"component.date": "Date",
|
"common.monet": "Monet",
|
||||||
"desktop.add_page": "Add page",
|
"desktop.page_index_format": "Desktop {0}",
|
||||||
"placement.fill": "Fill",
|
"launcher.title": "App Launcher",
|
||||||
"placement.fit": "Fit",
|
"launcher.subtitle": "Apps and folders from Windows Start Menu",
|
||||||
"placement.stretch": "Stretch",
|
"launcher.empty": "No Start Menu entries found.",
|
||||||
"placement.center": "Center",
|
"launcher.empty_folder": "This folder is empty.",
|
||||||
"placement.tile": "Tile"
|
"launcher.folder_items_format": "{0} apps",
|
||||||
|
"button.component_library": "Edit Desktop",
|
||||||
|
"tooltip.component_library": "Edit Desktop",
|
||||||
|
"component_library.title": "Widgets",
|
||||||
|
"component_library.empty": "Swipe to pick a category, tap to open, then drag a widget onto the desktop.",
|
||||||
|
"component_library.drag_hint": "Drag to place",
|
||||||
|
"component.delete": "Delete",
|
||||||
|
"component.edit": "Edit",
|
||||||
|
"component_category.date": "Calendar",
|
||||||
|
"component.date": "Calendar",
|
||||||
|
"component.month_calendar": "Month Calendar",
|
||||||
|
"component.lunar_calendar": "Lunar Calendar",
|
||||||
|
"desktop.add_page": "Add page",
|
||||||
|
"desktop.delete_page": "Delete page",
|
||||||
|
"placement.fill": "Fill",
|
||||||
|
"placement.fit": "Fit",
|
||||||
|
"placement.stretch": "Stretch",
|
||||||
|
"placement.center": "Center",
|
||||||
|
"placement.tile": "Tile"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,100 +1,118 @@
|
|||||||
{
|
{
|
||||||
"app.title": "LanMontainDesktop",
|
"app.title": "LanMontainDesktop",
|
||||||
"button.back_to_windows": "回到Windows",
|
"button.back_to_windows": "回到Windows",
|
||||||
"tooltip.back_to_windows": "回到Windows",
|
"tooltip.back_to_windows": "回到Windows",
|
||||||
"tooltip.open_settings": "设置",
|
"tooltip.open_settings": "设置",
|
||||||
"settings.title": "设置",
|
"settings.title": "设置",
|
||||||
"settings.back_to_desktop": "返回桌面",
|
"settings.back_to_desktop": "返回桌面",
|
||||||
"settings.nav_header": "设置选项",
|
"settings.nav_header": "设置选项",
|
||||||
"settings.nav.wallpaper": "壁纸",
|
"settings.nav.wallpaper": "壁纸",
|
||||||
"settings.nav.grid": "网格",
|
"settings.nav.grid": "网格",
|
||||||
"settings.nav.color": "颜色",
|
"settings.nav.color": "颜色",
|
||||||
"settings.nav.status_bar": "状态栏",
|
"settings.nav.status_bar": "状态栏",
|
||||||
"settings.nav.region": "地区",
|
"settings.nav.region": "地区",
|
||||||
"settings.wallpaper.title": "壁纸",
|
"settings.wallpaper.title": "壁纸",
|
||||||
"settings.wallpaper.description": "选择图片或视频后可立即设为应用窗口壁纸。",
|
"settings.wallpaper.description": "选择图片或视频后可立即设为应用窗口壁纸。",
|
||||||
"settings.wallpaper.current_label": "当前壁纸",
|
"settings.wallpaper.current_label": "当前壁纸",
|
||||||
"settings.wallpaper.placement_label": "显示方式",
|
"settings.wallpaper.placement_label": "显示方式",
|
||||||
"settings.wallpaper.pick_button": "浏览文件",
|
"settings.wallpaper.placement_desc": "调整图像在桌面上的填充方式。",
|
||||||
"settings.wallpaper.clear_button": "恢复纯色",
|
"settings.wallpaper.pick_button": "浏览文件",
|
||||||
"settings.wallpaper.no_selection": "未选择壁纸。",
|
"settings.wallpaper.clear_button": "恢复纯色",
|
||||||
"settings.wallpaper.storage_unavailable": "存储提供器不可用。",
|
"settings.wallpaper.no_selection": "未选择壁纸。",
|
||||||
"settings.wallpaper.import_failed": "导入壁纸文件失败。",
|
"settings.wallpaper.storage_unavailable": "存储提供器不可用。",
|
||||||
"settings.wallpaper.image_applied": "图片壁纸已应用。",
|
"settings.wallpaper.import_failed": "导入壁纸文件失败。",
|
||||||
"settings.wallpaper.video_applied": "视频壁纸已应用。",
|
"settings.wallpaper.image_applied": "图片壁纸已应用。",
|
||||||
"settings.wallpaper.unsupported_file": "所选文件类型不受支持。",
|
"settings.wallpaper.video_applied": "视频壁纸已应用。",
|
||||||
"settings.wallpaper.apply_failed_format": "应用壁纸失败:{0}",
|
"settings.wallpaper.unsupported_file": "所选文件类型不受支持。",
|
||||||
"settings.wallpaper.mode_format": "壁纸模式:{0}。",
|
"settings.wallpaper.apply_failed_format": "应用壁纸失败:{0}",
|
||||||
"settings.wallpaper.video_mode": "视频壁纸使用自动填充模式。",
|
"settings.wallpaper.mode_format": "壁纸模式:{0}。",
|
||||||
"settings.wallpaper.cleared": "背景已恢复为纯色。",
|
"settings.wallpaper.video_mode": "视频壁纸使用自动填充模式。",
|
||||||
"settings.wallpaper.default_status": "当前使用纯色背景。",
|
"settings.wallpaper.cleared": "背景已恢复为纯色。",
|
||||||
"settings.wallpaper.saved_not_found": "未找到已保存的壁纸文件,已使用纯色背景。",
|
"settings.wallpaper.default_status": "当前使用纯色背景。",
|
||||||
"settings.wallpaper.restored": "已恢复保存的壁纸。",
|
"settings.wallpaper.saved_not_found": "未找到已保存的壁纸文件,已使用纯色背景。",
|
||||||
"settings.wallpaper.video_restored": "已恢复保存的视频壁纸。",
|
"settings.wallpaper.restored": "已恢复保存的壁纸。",
|
||||||
"settings.wallpaper.restore_failed": "恢复已保存壁纸失败,已使用纯色背景。",
|
"settings.wallpaper.video_restored": "已恢复保存的视频壁纸。",
|
||||||
"settings.wallpaper.video_not_found": "未找到视频壁纸文件。",
|
"settings.wallpaper.restore_failed": "恢复已保存壁纸失败,已使用纯色背景。",
|
||||||
"settings.wallpaper.video_player_unavailable": "视频播放器不可用。",
|
"settings.wallpaper.video_not_found": "未找到视频壁纸文件。",
|
||||||
"settings.wallpaper.video_play_failed_format": "播放视频壁纸失败:{0}",
|
"settings.wallpaper.video_player_unavailable": "视频播放器不可用。",
|
||||||
"settings.grid.title": "网格布局",
|
"settings.wallpaper.video_play_failed_format": "播放视频壁纸失败:{0}",
|
||||||
"settings.grid.description": "每个组件至少占用一个格子(最小 1x1)。",
|
"settings.grid.title": "网格布局",
|
||||||
"settings.grid.short_side_label": "短边格数",
|
"settings.grid.description": "每个组件至少占用一个格子(最小 1x1)。",
|
||||||
"settings.grid.apply_button": "应用",
|
"settings.grid.short_side_label": "短边格数",
|
||||||
"settings.grid.info_format": "网格:{0} 列 x {1} 行 | 单元格 {2:F1}px(1:1)",
|
"settings.grid.spacing_label": "网格间距",
|
||||||
"settings.color.title": "颜色",
|
"settings.grid.spacing_relaxed": "宽松(iOS)",
|
||||||
"settings.color.description": "切换日夜模式并选择应用主题色。",
|
"settings.grid.spacing_compact": "紧凑(Android)",
|
||||||
"settings.color.day_night_label": "日夜模式",
|
"settings.grid.edge_inset_label": "屏幕边距",
|
||||||
"settings.color.day_night_on": "夜间",
|
"settings.grid.edge_inset_px_format": "≈ {0:F1}px",
|
||||||
"settings.color.day_night_off": "日间",
|
"settings.grid.apply_button": "应用",
|
||||||
"settings.color.recommended_label": "推荐色",
|
"settings.grid.info_format": "网格:{0} 列 x {1} 行 | 单元格 {2:F1}px(1:1)",
|
||||||
"settings.color.system_monet_label": "系统莫奈色",
|
"settings.color.title": "颜色",
|
||||||
"settings.color.refresh_button": "刷新",
|
"settings.color.description": "切换日夜模式并选择应用主题色。",
|
||||||
"settings.color.mode_night": "夜间模式已启用",
|
"settings.color.day_night_label": "日夜模式",
|
||||||
"settings.color.mode_day": "日间模式已启用",
|
"settings.color.day_night_on": "夜间",
|
||||||
"settings.color.mode_status_format": "主题模式:{0}。",
|
"settings.color.day_night_off": "日间",
|
||||||
"settings.color.monet_refreshed": "莫奈色已刷新。",
|
"settings.color.recommended_label": "推荐色",
|
||||||
"settings.color.theme_ready_format": "主题色已就绪:{0}。",
|
"settings.color.system_monet_label": "系统莫奈色",
|
||||||
"settings.color.theme_applied_format": "{0}主题色已应用:{1}。",
|
"settings.color.refresh_button": "刷新",
|
||||||
"settings.color.theme_updated_wallpaper": "壁纸已更新,莫奈色已刷新。",
|
"settings.color.mode_night": "夜间模式已启用",
|
||||||
"settings.color.theme_updated_video": "视频壁纸已更新,主题色已刷新。",
|
"settings.color.mode_day": "日间模式已启用",
|
||||||
"settings.color.theme_cleared_wallpaper": "壁纸已清除,莫奈色已刷新。",
|
"settings.color.mode_status_format": "主题模式:{0}。",
|
||||||
"settings.status_bar.title": "状态栏",
|
"settings.color.monet_refreshed": "莫奈色已刷新。",
|
||||||
"settings.status_bar.description": "选择顶部状态栏显示的组件。",
|
"settings.color.theme_ready_format": "主题色已就绪:{0}。",
|
||||||
"settings.status_bar.clock_header": "时间组件",
|
"settings.color.theme_applied_format": "{0}主题色已应用:{1}。",
|
||||||
"settings.status_bar.clock_description": "在顶部状态栏显示时钟。",
|
"settings.color.theme_updated_wallpaper": "壁纸已更新,莫奈色已刷新。",
|
||||||
"settings.region.title": "地区",
|
"settings.color.theme_updated_video": "视频壁纸已更新,主题色已刷新。",
|
||||||
"settings.region.description": "选择语言并立即应用到设置与主要界面。",
|
"settings.color.theme_cleared_wallpaper": "壁纸已清除,莫奈色已刷新。",
|
||||||
"settings.region.language_header": "语言",
|
"settings.status_bar.title": "状态栏",
|
||||||
"settings.region.language_label": "语言",
|
"settings.status_bar.description": "选择顶部状态栏显示的组件。",
|
||||||
"settings.region.language_zh": "中文",
|
"settings.status_bar.clock_header": "时间组件",
|
||||||
"settings.region.language_en": "英文",
|
"settings.status_bar.clock_description": "在顶部状态栏显示时钟。",
|
||||||
"settings.region.applied_format": "语言已切换为:{0}",
|
"settings.status_bar.spacing_header": "组件间距",
|
||||||
"settings.footer": "LanMontainDesktop 设置",
|
"settings.status_bar.spacing_desc": "调整状态栏组件之间的间距。",
|
||||||
"filepicker.title": "选择壁纸",
|
"settings.status_bar.spacing_mode_compact": "紧凑",
|
||||||
"filepicker.image_files": "图片文件",
|
"settings.status_bar.spacing_mode_relaxed": "宽松",
|
||||||
"filepicker.video_files": "视频文件",
|
"settings.status_bar.spacing_mode_custom": "自定义",
|
||||||
"common.day": "日间",
|
"settings.status_bar.spacing_custom_label": "自定义间距(%)",
|
||||||
"common.night": "夜间",
|
"settings.status_bar.spacing_custom_px_format": "≈ {0:F1}px",
|
||||||
"common.back": "返回",
|
"settings.region.title": "地区",
|
||||||
"common.close": "关闭",
|
"settings.region.description": "选择语言并立即应用到设置与主要界面。",
|
||||||
"common.recommended": "推荐",
|
"settings.region.language_header": "语言",
|
||||||
"common.monet": "莫奈",
|
"settings.region.language_label": "语言",
|
||||||
"desktop.page_index_format": "桌面 {0}",
|
"settings.region.language_zh": "中文",
|
||||||
"launcher.title": "应用启动台",
|
"settings.region.language_en": "英文",
|
||||||
"launcher.subtitle": "按 Windows 开始菜单结构显示所有应用与文件夹",
|
"settings.region.applied_format": "语言已切换为:{0}",
|
||||||
"launcher.empty": "未找到开始菜单条目。",
|
"settings.footer": "LanMontainDesktop 设置",
|
||||||
"launcher.empty_folder": "此文件夹为空。",
|
"filepicker.title": "选择壁纸",
|
||||||
"launcher.folder_items_format": "{0} 个应用",
|
"filepicker.image_files": "图片文件",
|
||||||
"button.component_library": "桌面编辑",
|
"filepicker.video_files": "视频文件",
|
||||||
"tooltip.component_library": "桌面编辑",
|
"common.day": "日间",
|
||||||
"component_library.title": "桌面编辑",
|
"common.night": "夜间",
|
||||||
"component_library.empty": "左右滑动选择类别,点击进入,然后拖动组件到桌面放置。",
|
"common.back": "返回",
|
||||||
"component_library.drag_hint": "拖动放置",
|
"common.close": "关闭",
|
||||||
"component_category.date": "日期",
|
"common.recommended": "推荐",
|
||||||
"component.date": "日历",
|
"common.monet": "莫奈",
|
||||||
"desktop.add_page": "新增页面",
|
"desktop.page_index_format": "桌面 {0}",
|
||||||
"placement.fill": "填充",
|
"launcher.title": "应用启动台",
|
||||||
"placement.fit": "适应",
|
"launcher.subtitle": "按 Windows 开始菜单结构显示所有应用与文件夹",
|
||||||
"placement.stretch": "拉伸",
|
"launcher.empty": "未找到开始菜单条目。",
|
||||||
"placement.center": "居中",
|
"launcher.empty_folder": "此文件夹为空。",
|
||||||
"placement.tile": "平铺"
|
"launcher.folder_items_format": "{0} 个应用",
|
||||||
|
"button.component_library": "桌面编辑",
|
||||||
|
"tooltip.component_library": "桌面编辑",
|
||||||
|
"component_library.title": "桌面编辑",
|
||||||
|
"component_library.empty": "左右滑动选择类别,点击进入,然后拖动组件到桌面放置。",
|
||||||
|
"component_library.drag_hint": "拖动放置",
|
||||||
|
"component.delete": "删除",
|
||||||
|
"component.edit": "编辑",
|
||||||
|
"component_category.date": "日历",
|
||||||
|
"component.date": "日历",
|
||||||
|
"component.month_calendar": "月历",
|
||||||
|
"component.lunar_calendar": "农历",
|
||||||
|
"desktop.add_page": "新增页面",
|
||||||
|
"desktop.delete_page": "删除页面",
|
||||||
|
"placement.fill": "填充",
|
||||||
|
"placement.fit": "适应",
|
||||||
|
"placement.stretch": "拉伸",
|
||||||
|
"placement.center": "居中",
|
||||||
|
"placement.tile": "平铺"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ public sealed class AppSettingsSnapshot
|
|||||||
{
|
{
|
||||||
public int GridShortSideCells { get; set; } = 12;
|
public int GridShortSideCells { get; set; } = 12;
|
||||||
|
|
||||||
|
public string GridSpacingPreset { get; set; } = "Relaxed";
|
||||||
|
|
||||||
|
public int DesktopEdgeInsetPercent { get; set; } = 18;
|
||||||
|
|
||||||
public bool? IsNightMode { get; set; }
|
public bool? IsNightMode { get; set; }
|
||||||
|
|
||||||
public string? ThemeColor { get; set; }
|
public string? ThemeColor { get; set; }
|
||||||
@@ -28,10 +32,16 @@ public sealed class AppSettingsSnapshot
|
|||||||
TaskbarActionId.OpenSettings.ToString()
|
TaskbarActionId.OpenSettings.ToString()
|
||||||
];
|
];
|
||||||
|
|
||||||
public bool EnableDynamicTaskbarActions { get; set; } = false;
|
public bool EnableDynamicTaskbarActions { get; set; } = true;
|
||||||
|
|
||||||
public string TaskbarLayoutMode { get; set; } = "BottomFullRowMacStyle";
|
public string TaskbarLayoutMode { get; set; } = "BottomFullRowMacStyle";
|
||||||
|
|
||||||
|
public string ClockDisplayFormat { get; set; } = "HourMinuteSecond";
|
||||||
|
|
||||||
|
public string StatusBarSpacingMode { get; set; } = "Relaxed";
|
||||||
|
|
||||||
|
public int StatusBarCustomSpacingPercent { get; set; } = 12;
|
||||||
|
|
||||||
public int DesktopPageCount { get; set; } = 1;
|
public int DesktopPageCount { get; set; } = 1;
|
||||||
|
|
||||||
public int CurrentDesktopSurfaceIndex { get; set; } = 0;
|
public int CurrentDesktopSurfaceIndex { get; set; } = 0;
|
||||||
|
|||||||
@@ -4,5 +4,8 @@ public enum TaskbarActionId
|
|||||||
{
|
{
|
||||||
MinimizeToWindows,
|
MinimizeToWindows,
|
||||||
OpenSettings,
|
OpenSettings,
|
||||||
AddDesktopPage
|
AddDesktopPage,
|
||||||
|
DeleteDesktopPage,
|
||||||
|
DeleteComponent,
|
||||||
|
EditComponent
|
||||||
}
|
}
|
||||||
|
|||||||
227
LanMontainDesktop/Services/LunarCalendarService.cs
Normal file
227
LanMontainDesktop/Services/LunarCalendarService.cs
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace LanMontainDesktop.Services;
|
||||||
|
|
||||||
|
public sealed class LunarCalendarService
|
||||||
|
{
|
||||||
|
private static readonly ChineseLunisolarCalendar Calendar = new();
|
||||||
|
|
||||||
|
private static readonly string[] HeavenlyStemsZh =
|
||||||
|
[
|
||||||
|
"\u7532",
|
||||||
|
"\u4e59",
|
||||||
|
"\u4e19",
|
||||||
|
"\u4e01",
|
||||||
|
"\u620a",
|
||||||
|
"\u5df1",
|
||||||
|
"\u5e9a",
|
||||||
|
"\u8f9b",
|
||||||
|
"\u58ec",
|
||||||
|
"\u7678"
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly string[] EarthlyBranchesZh =
|
||||||
|
[
|
||||||
|
"\u5b50",
|
||||||
|
"\u4e11",
|
||||||
|
"\u5bc5",
|
||||||
|
"\u536f",
|
||||||
|
"\u8fb0",
|
||||||
|
"\u5df3",
|
||||||
|
"\u5348",
|
||||||
|
"\u672a",
|
||||||
|
"\u7533",
|
||||||
|
"\u9149",
|
||||||
|
"\u620c",
|
||||||
|
"\u4ea5"
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly string[] HeavenlyStemsEn =
|
||||||
|
["Jia", "Yi", "Bing", "Ding", "Wu", "Ji", "Geng", "Xin", "Ren", "Gui"];
|
||||||
|
|
||||||
|
private static readonly string[] EarthlyBranchesEn =
|
||||||
|
["Zi", "Chou", "Yin", "Mao", "Chen", "Si", "Wu", "Wei", "Shen", "You", "Xu", "Hai"];
|
||||||
|
|
||||||
|
private static readonly string[] ZodiacsZh =
|
||||||
|
[
|
||||||
|
"\u9f20",
|
||||||
|
"\u725b",
|
||||||
|
"\u864e",
|
||||||
|
"\u5154",
|
||||||
|
"\u9f99",
|
||||||
|
"\u86c7",
|
||||||
|
"\u9a6c",
|
||||||
|
"\u7f8a",
|
||||||
|
"\u7334",
|
||||||
|
"\u9e21",
|
||||||
|
"\u72d7",
|
||||||
|
"\u732a"
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly string[] ZodiacsEn =
|
||||||
|
["Rat", "Ox", "Tiger", "Rabbit", "Dragon", "Snake", "Horse", "Goat", "Monkey", "Rooster", "Dog", "Pig"];
|
||||||
|
|
||||||
|
private static readonly string[] LunarMonthsZh =
|
||||||
|
[
|
||||||
|
"\u6b63",
|
||||||
|
"\u4e8c",
|
||||||
|
"\u4e09",
|
||||||
|
"\u56db",
|
||||||
|
"\u4e94",
|
||||||
|
"\u516d",
|
||||||
|
"\u4e03",
|
||||||
|
"\u516b",
|
||||||
|
"\u4e5d",
|
||||||
|
"\u5341",
|
||||||
|
"\u51ac",
|
||||||
|
"\u814a"
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly string[] LunarDayDigitsZh =
|
||||||
|
[
|
||||||
|
"\u4e00",
|
||||||
|
"\u4e8c",
|
||||||
|
"\u4e09",
|
||||||
|
"\u56db",
|
||||||
|
"\u4e94",
|
||||||
|
"\u516d",
|
||||||
|
"\u4e03",
|
||||||
|
"\u516b",
|
||||||
|
"\u4e5d",
|
||||||
|
"\u5341"
|
||||||
|
];
|
||||||
|
|
||||||
|
public LunarCalendarInfo GetLunarInfo(DateTime dateTime)
|
||||||
|
{
|
||||||
|
var date = dateTime.Date;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var lunarYear = Calendar.GetYear(date);
|
||||||
|
var rawLunarMonth = Calendar.GetMonth(date);
|
||||||
|
var lunarDay = Calendar.GetDayOfMonth(date);
|
||||||
|
|
||||||
|
var (lunarMonth, isLeapMonth) = NormalizeLunarMonth(lunarYear, rawLunarMonth);
|
||||||
|
|
||||||
|
var sexagenaryYear = Calendar.GetSexagenaryYear(date);
|
||||||
|
var stemIndex = Calendar.GetCelestialStem(sexagenaryYear) - 1;
|
||||||
|
var branchIndex = Calendar.GetTerrestrialBranch(sexagenaryYear) - 1;
|
||||||
|
|
||||||
|
var ganzhiYearZh = $"{HeavenlyStemsZh[stemIndex]}{EarthlyBranchesZh[branchIndex]}";
|
||||||
|
var ganzhiYearEn = $"{HeavenlyStemsEn[stemIndex]}-{EarthlyBranchesEn[branchIndex]}";
|
||||||
|
var zodiacZh = ZodiacsZh[branchIndex];
|
||||||
|
var zodiacEn = ZodiacsEn[branchIndex];
|
||||||
|
|
||||||
|
return new LunarCalendarInfo(
|
||||||
|
LunarYear: lunarYear,
|
||||||
|
LunarMonth: lunarMonth,
|
||||||
|
LunarDay: lunarDay,
|
||||||
|
IsLeapMonth: isLeapMonth,
|
||||||
|
LunarDateZh: BuildLunarDateZh(lunarMonth, lunarDay, isLeapMonth),
|
||||||
|
LunarDateEn: BuildLunarDateEn(lunarMonth, lunarDay, isLeapMonth),
|
||||||
|
GanzhiYearZh: ganzhiYearZh,
|
||||||
|
GanzhiYearEn: ganzhiYearEn,
|
||||||
|
ZodiacZh: zodiacZh,
|
||||||
|
ZodiacEn: zodiacEn);
|
||||||
|
}
|
||||||
|
catch (ArgumentOutOfRangeException)
|
||||||
|
{
|
||||||
|
// ChineseLunisolarCalendar has a limited date range.
|
||||||
|
return new LunarCalendarInfo(
|
||||||
|
LunarYear: date.Year,
|
||||||
|
LunarMonth: date.Month,
|
||||||
|
LunarDay: date.Day,
|
||||||
|
IsLeapMonth: false,
|
||||||
|
LunarDateZh: "\u65e5\u671f\u8d85\u51fa\u519c\u5386\u652f\u6301\u8303\u56f4",
|
||||||
|
LunarDateEn: date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture),
|
||||||
|
GanzhiYearZh: "-",
|
||||||
|
GanzhiYearEn: "-",
|
||||||
|
ZodiacZh: "-",
|
||||||
|
ZodiacEn: "-");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (int Month, bool IsLeapMonth) NormalizeLunarMonth(int lunarYear, int rawMonth)
|
||||||
|
{
|
||||||
|
var leapMonth = Calendar.GetLeapMonth(lunarYear);
|
||||||
|
if (leapMonth == 0)
|
||||||
|
{
|
||||||
|
return (rawMonth, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawMonth == leapMonth)
|
||||||
|
{
|
||||||
|
return (rawMonth - 1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawMonth > leapMonth)
|
||||||
|
{
|
||||||
|
return (rawMonth - 1, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (rawMonth, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildLunarDateZh(int lunarMonth, int lunarDay, bool isLeapMonth)
|
||||||
|
{
|
||||||
|
var monthName = lunarMonth is >= 1 and <= 12
|
||||||
|
? LunarMonthsZh[lunarMonth - 1]
|
||||||
|
: lunarMonth.ToString(CultureInfo.InvariantCulture);
|
||||||
|
var leapPrefix = isLeapMonth ? "\u95f0" : string.Empty;
|
||||||
|
return $"{leapPrefix}{monthName}\u6708{BuildLunarDayZh(lunarDay)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildLunarDateEn(int lunarMonth, int lunarDay, bool isLeapMonth)
|
||||||
|
{
|
||||||
|
var leapPrefix = isLeapMonth ? "Leap " : string.Empty;
|
||||||
|
return $"{leapPrefix}M{lunarMonth} D{lunarDay}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildLunarDayZh(int day)
|
||||||
|
{
|
||||||
|
if (day <= 0)
|
||||||
|
{
|
||||||
|
return day.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (day <= 10)
|
||||||
|
{
|
||||||
|
return day == 10 ? "\u521d\u5341" : $"\u521d{LunarDayDigitsZh[day - 1]}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (day < 20)
|
||||||
|
{
|
||||||
|
return $"\u5341{LunarDayDigitsZh[day - 11]}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (day == 20)
|
||||||
|
{
|
||||||
|
return "\u4e8c\u5341";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (day < 30)
|
||||||
|
{
|
||||||
|
return $"\u5eff{LunarDayDigitsZh[day - 21]}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (day == 30)
|
||||||
|
{
|
||||||
|
return "\u4e09\u5341";
|
||||||
|
}
|
||||||
|
|
||||||
|
return day.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record LunarCalendarInfo(
|
||||||
|
int LunarYear,
|
||||||
|
int LunarMonth,
|
||||||
|
int LunarDay,
|
||||||
|
bool IsLeapMonth,
|
||||||
|
string LunarDateZh,
|
||||||
|
string LunarDateEn,
|
||||||
|
string GanzhiYearZh,
|
||||||
|
string GanzhiYearEn,
|
||||||
|
string ZodiacZh,
|
||||||
|
string ZodiacEn);
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<Setter Property="Background" Value="{DynamicResource AdaptiveButtonBackgroundBrush}" />
|
<Setter Property="Background" Value="{DynamicResource AdaptiveButtonBackgroundBrush}" />
|
||||||
<Setter Property="BorderThickness" Value="1" />
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
<Setter Property="BorderBrush" Value="{DynamicResource AdaptiveButtonBorderBrush}" />
|
<Setter Property="BorderBrush" Value="{DynamicResource AdaptiveButtonBorderBrush}" />
|
||||||
<Setter Property="CornerRadius" Value="6" />
|
<Setter Property="CornerRadius" Value="20" />
|
||||||
<Setter Property="Foreground" Value="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
<Setter Property="Foreground" Value="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||||
<Setter Property="FontSize" Value="14" />
|
<Setter Property="FontSize" Value="14" />
|
||||||
<Setter Property="Padding" Value="16,10" />
|
<Setter Property="Padding" Value="16,10" />
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
|
|
||||||
<Style Selector="Button.swatch-button">
|
<Style Selector="Button.swatch-button">
|
||||||
<Setter Property="BorderThickness" Value="0" />
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
<Setter Property="CornerRadius" Value="10" />
|
<Setter Property="CornerRadius" Value="16" />
|
||||||
<Setter Property="Opacity" Value="0.88" />
|
<Setter Property="Opacity" Value="0.88" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
@@ -70,25 +70,39 @@
|
|||||||
<Style Selector="Border.glass-panel">
|
<Style Selector="Border.glass-panel">
|
||||||
<Setter Property="Background" Value="{DynamicResource AdaptiveGlassPanelBackgroundBrush}" />
|
<Setter Property="Background" Value="{DynamicResource AdaptiveGlassPanelBackgroundBrush}" />
|
||||||
<Setter Property="BorderBrush" Value="{DynamicResource AdaptiveGlassPanelBorderBrush}" />
|
<Setter Property="BorderBrush" Value="{DynamicResource AdaptiveGlassPanelBorderBrush}" />
|
||||||
<Setter Property="BorderThickness" Value="1" />
|
<Setter Property="BorderThickness" Value="1.2" />
|
||||||
<Setter Property="CornerRadius" Value="8" />
|
<Setter Property="CornerRadius" Value="28" />
|
||||||
<Setter Property="Opacity" Value="{DynamicResource AdaptiveGlassPanelOpacity}" />
|
<Setter Property="Opacity" Value="{DynamicResource AdaptiveGlassPanelOpacity}" />
|
||||||
<Setter Property="BoxShadow" Value="0 1 2 #1A000000" />
|
<Setter Property="BoxShadow" Value="0 4 12 #1A000000" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style Selector="Border.glass-strong">
|
<Style Selector="Border.glass-strong">
|
||||||
<Setter Property="Background" Value="{DynamicResource AdaptiveGlassStrongBackgroundBrush}" />
|
<Setter Property="Background" Value="{DynamicResource AdaptiveGlassStrongBackgroundBrush}" />
|
||||||
<Setter Property="BorderBrush" Value="{DynamicResource AdaptiveGlassStrongBorderBrush}" />
|
<Setter Property="BorderBrush" Value="{DynamicResource AdaptiveGlassStrongBorderBrush}" />
|
||||||
<Setter Property="BorderThickness" Value="1" />
|
<Setter Property="BorderThickness" Value="1.5" />
|
||||||
<Setter Property="CornerRadius" Value="12" />
|
<Setter Property="CornerRadius" Value="32" />
|
||||||
<Setter Property="Opacity" Value="{DynamicResource AdaptiveGlassStrongOpacity}" />
|
<Setter Property="Opacity" Value="{DynamicResource AdaptiveGlassStrongOpacity}" />
|
||||||
<Setter Property="BoxShadow" Value="0 2 4 #26000000" />
|
<Setter Property="BoxShadow" Value="0 8 24 #26000000" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Border.glass-island">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource AdaptiveGlassStrongBackgroundBrush}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource AdaptiveGlassStrongBorderBrush}" />
|
||||||
|
<Setter Property="BorderThickness" Value="1.5" />
|
||||||
|
<Setter Property="CornerRadius" Value="36" />
|
||||||
|
<Setter Property="Opacity" Value="{DynamicResource AdaptiveGlassStrongOpacity}" />
|
||||||
|
<Setter Property="BoxShadow" Value="0 12 32 #33000000" />
|
||||||
|
<Setter Property="Transitions">
|
||||||
|
<Transitions>
|
||||||
|
<ThicknessTransition Property="Padding" Duration="0:0:0.2" Easing="QuarticEaseOut" />
|
||||||
|
</Transitions>
|
||||||
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style Selector="Border.mica-strong">
|
<Style Selector="Border.mica-strong">
|
||||||
<Setter Property="Background" Value="{DynamicResource AdaptiveGlassStrongBackgroundBrush}" />
|
<Setter Property="Background" Value="{DynamicResource AdaptiveGlassStrongBackgroundBrush}" />
|
||||||
<Setter Property="BorderThickness" Value="0" />
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
<Setter Property="CornerRadius" Value="12" />
|
<Setter Property="CornerRadius" Value="36" />
|
||||||
<Setter Property="Opacity" Value="{DynamicResource AdaptiveGlassStrongOpacity}" />
|
<Setter Property="Opacity" Value="{DynamicResource AdaptiveGlassStrongOpacity}" />
|
||||||
<Setter Property="BoxShadow" Value="0 8 22 #2A000000" />
|
<Setter Property="BoxShadow" Value="0 8 22 #2A000000" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|||||||
@@ -3,21 +3,31 @@
|
|||||||
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"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignWidth="220"
|
d:DesignWidth="180"
|
||||||
d:DesignHeight="70"
|
d:DesignHeight="48"
|
||||||
x:Class="LanMontainDesktop.Views.Components.ClockWidget">
|
x:Class="LanMontainDesktop.Views.Components.ClockWidget">
|
||||||
|
|
||||||
<Border x:Name="RootBorder"
|
<Border x:Name="RootBorder"
|
||||||
Classes="glass-panel"
|
Classes="glass-panel"
|
||||||
Padding="8"
|
Padding="0"
|
||||||
CornerRadius="8">
|
CornerRadius="24">
|
||||||
<TextBlock x:Name="TimeTextBlock"
|
<StackPanel Orientation="Horizontal"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center">
|
||||||
TextAlignment="Center"
|
<TextBlock x:Name="MainTimeTextBlock"
|
||||||
FontSize="26"
|
FontFeatures="tnum"
|
||||||
FontWeight="SemiBold"
|
FontSize="20"
|
||||||
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
FontWeight="SemiBold"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||||
|
<TextBlock x:Name="SecondsTextBlock"
|
||||||
|
FontFeatures="tnum"
|
||||||
|
FontSize="14"
|
||||||
|
FontWeight="Normal"
|
||||||
|
Opacity="0.75"
|
||||||
|
Margin="4,2,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
|
||||||
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ using LanMontainDesktop.Services;
|
|||||||
|
|
||||||
namespace LanMontainDesktop.Views.Components;
|
namespace LanMontainDesktop.Views.Components;
|
||||||
|
|
||||||
|
public enum ClockDisplayFormat
|
||||||
|
{
|
||||||
|
HourMinuteSecond, // HH:mm:ss
|
||||||
|
HourMinute // HH:mm
|
||||||
|
}
|
||||||
|
|
||||||
public partial class ClockWidget : UserControl
|
public partial class ClockWidget : UserControl
|
||||||
{
|
{
|
||||||
private readonly DispatcherTimer _timer = new()
|
private readonly DispatcherTimer _timer = new()
|
||||||
@@ -16,6 +22,7 @@ public partial class ClockWidget : UserControl
|
|||||||
};
|
};
|
||||||
|
|
||||||
private TimeZoneService? _timeZoneService;
|
private TimeZoneService? _timeZoneService;
|
||||||
|
private ClockDisplayFormat _displayFormat = ClockDisplayFormat.HourMinuteSecond;
|
||||||
|
|
||||||
public ClockWidget()
|
public ClockWidget()
|
||||||
{
|
{
|
||||||
@@ -27,9 +34,21 @@ public partial class ClockWidget : UserControl
|
|||||||
UpdateClock();
|
UpdateClock();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public ClockDisplayFormat DisplayFormat
|
||||||
/// 设置时区服务
|
{
|
||||||
/// </summary>
|
get => _displayFormat;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_displayFormat = value;
|
||||||
|
UpdateClock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetDisplayFormat(ClockDisplayFormat format)
|
||||||
|
{
|
||||||
|
DisplayFormat = format;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetTimeZoneService(TimeZoneService timeZoneService)
|
public void SetTimeZoneService(TimeZoneService timeZoneService)
|
||||||
{
|
{
|
||||||
if (_timeZoneService != null)
|
if (_timeZoneService != null)
|
||||||
@@ -66,17 +85,45 @@ public partial class ClockWidget : UserControl
|
|||||||
private void UpdateClock()
|
private void UpdateClock()
|
||||||
{
|
{
|
||||||
var now = _timeZoneService?.GetCurrentTime() ?? DateTime.Now;
|
var now = _timeZoneService?.GetCurrentTime() ?? DateTime.Now;
|
||||||
TimeTextBlock.Text = now.ToString("HH:mm:ss", CultureInfo.CurrentCulture);
|
|
||||||
|
MainTimeTextBlock.Text = now.ToString("HH:mm", CultureInfo.CurrentCulture);
|
||||||
|
SecondsTextBlock.Text = now.ToString("ss", CultureInfo.CurrentCulture);
|
||||||
|
|
||||||
|
SecondsTextBlock.IsVisible = _displayFormat == ClockDisplayFormat.HourMinuteSecond;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyCellSize(double cellSize)
|
public void ApplyCellSize(double cellSize)
|
||||||
{
|
{
|
||||||
var padding = Math.Clamp(cellSize * 0.12, 2, 14);
|
// --- Class Island “满盈”风格算法 ---
|
||||||
RootBorder.Padding = new Thickness(padding);
|
|
||||||
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(cellSize * 0.16, 4, 18));
|
// 1. 计算组件高度:保持与任务栏核心比例一致 (0.74x)
|
||||||
|
var targetHeight = Math.Clamp(cellSize * 0.74, 34, 74);
|
||||||
|
RootBorder.Height = targetHeight;
|
||||||
|
|
||||||
|
// 2. 动态圆角:确保始终是完美的胶囊半圆
|
||||||
|
RootBorder.CornerRadius = new CornerRadius(targetHeight / 2);
|
||||||
|
RootBorder.VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center;
|
||||||
|
|
||||||
|
// 3. 核心:满盈字阶 (Filled Typography)
|
||||||
|
// 使主时间文字占据容器高度的 ~68%,产生饱满的视觉张力
|
||||||
|
var mainFontSize = targetHeight * 0.68;
|
||||||
|
MainTimeTextBlock.FontSize = mainFontSize;
|
||||||
|
MainTimeTextBlock.FontWeight = FontWeight.SemiBold;
|
||||||
|
|
||||||
|
// 4. 次级信息:秒数维持 0.7x 比例,并增强透明度呼吸感
|
||||||
|
SecondsTextBlock.FontSize = mainFontSize * 0.7;
|
||||||
|
SecondsTextBlock.Opacity = 0.55;
|
||||||
|
|
||||||
|
// 5. 视觉占比:占据约 2.2 个单元格的感官宽度 (cellSize * 2 + gaps)
|
||||||
|
RootBorder.MinWidth = cellSize * 2.2;
|
||||||
|
|
||||||
// Keep the time legible across dense and sparse grid layouts.
|
// 6. 间距微调
|
||||||
TimeTextBlock.FontSize = Math.Clamp(cellSize * 0.42, 10, 56);
|
if (MainTimeTextBlock.Parent is StackPanel panel)
|
||||||
TimeTextBlock.FontWeight = FontWeight.SemiBold;
|
{
|
||||||
|
panel.Spacing = Math.Clamp(cellSize * 0.06, 2, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保清除可能存在的固定 Padding,由代码控制“紧密感”
|
||||||
|
RootBorder.Padding = new Thickness(Math.Clamp(cellSize * 0.15, 12, 24), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,81 +3,111 @@
|
|||||||
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"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignWidth="400"
|
d:DesignWidth="460"
|
||||||
d:DesignHeight="200"
|
d:DesignHeight="220"
|
||||||
x:Class="LanMontainDesktop.Views.Components.DateWidget">
|
x:Class="LanMontainDesktop.Views.Components.DateWidget">
|
||||||
|
|
||||||
<Border x:Name="RootBorder"
|
<Border x:Name="RootBorder"
|
||||||
Background="Transparent"
|
Background="{DynamicResource AdaptiveSurfaceBaseBrush}"
|
||||||
CornerRadius="16"
|
CornerRadius="28"
|
||||||
ClipToBounds="True">
|
ClipToBounds="True"
|
||||||
<Grid ColumnDefinitions="*,*">
|
Padding="12">
|
||||||
<!-- 左侧:月历 -->
|
<Viewbox Stretch="Uniform">
|
||||||
<Border x:Name="CalendarBackgroundBorder"
|
<Grid Width="460"
|
||||||
Grid.Column="0"
|
Height="220"
|
||||||
Padding="12"
|
ColumnDefinitions="1.2*,1*"
|
||||||
Background="{DynamicResource AdaptiveSurfaceBaseBrush}">
|
ColumnSpacing="12">
|
||||||
<Grid RowDefinitions="Auto,*">
|
|
||||||
<!-- 月份年份 -->
|
|
||||||
<TextBlock x:Name="CalendarMonthYearTextBlock"
|
|
||||||
Grid.Row="0"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
FontSize="12"
|
|
||||||
FontWeight="SemiBold"
|
|
||||||
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
|
||||||
Margin="0,0,0,8" />
|
|
||||||
|
|
||||||
<!-- 月历网格 -->
|
|
||||||
<Grid x:Name="CalendarGrid"
|
|
||||||
Grid.Row="1"
|
|
||||||
RowDefinitions="Auto,*,*,*,*,*"
|
|
||||||
ColumnDefinitions="*,*,*,*,*,*,*">
|
|
||||||
<!-- 星期标题 -->
|
|
||||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="日" HorizontalAlignment="Center" FontSize="10" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
|
|
||||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="一" HorizontalAlignment="Center" FontSize="10" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
|
|
||||||
<TextBlock Grid.Row="0" Grid.Column="2" Text="二" HorizontalAlignment="Center" FontSize="10" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
|
|
||||||
<TextBlock Grid.Row="0" Grid.Column="3" Text="三" HorizontalAlignment="Center" FontSize="10" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
|
|
||||||
<TextBlock Grid.Row="0" Grid.Column="4" Text="四" HorizontalAlignment="Center" FontSize="10" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
|
|
||||||
<TextBlock Grid.Row="0" Grid.Column="5" Text="五" HorizontalAlignment="Center" FontSize="10" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
|
|
||||||
<TextBlock Grid.Row="0" Grid.Column="6" Text="六" HorizontalAlignment="Center" FontSize="10" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- 右侧:今日详情 -->
|
<Grid x:Name="LeftPanelGrid"
|
||||||
<Border x:Name="TodayBackgroundBorder"
|
Grid.Column="0"
|
||||||
Grid.Column="1"
|
RowDefinitions="Auto,Auto,*"
|
||||||
Background="{DynamicResource AdaptiveAccentBrush}"
|
RowSpacing="8">
|
||||||
Padding="16">
|
<TextBlock x:Name="GregorianHeadlineTextBlock"
|
||||||
<Grid RowDefinitions="Auto,*,Auto">
|
Grid.Row="0"
|
||||||
<!-- 今日标签 -->
|
FontSize="22"
|
||||||
<TextBlock Grid.Row="0"
|
FontWeight="Bold"
|
||||||
Text="今天"
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||||
HorizontalAlignment="Center"
|
|
||||||
FontSize="11"
|
<UniformGrid Grid.Row="1"
|
||||||
FontWeight="Medium"
|
Columns="7">
|
||||||
Opacity="0.8"
|
<TextBlock x:Name="WeekdayText0" Text="日" HorizontalAlignment="Center" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" FontSize="13" FontWeight="SemiBold" />
|
||||||
Foreground="{DynamicResource AdaptiveOnAccentBrush}" />
|
<TextBlock x:Name="WeekdayText1" Text="一" HorizontalAlignment="Center" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" FontSize="13" FontWeight="SemiBold" />
|
||||||
|
<TextBlock x:Name="WeekdayText2" Text="二" HorizontalAlignment="Center" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" FontSize="13" FontWeight="SemiBold" />
|
||||||
<!-- 日期数字 -->
|
<TextBlock x:Name="WeekdayText3" Text="三" HorizontalAlignment="Center" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" FontSize="13" FontWeight="SemiBold" />
|
||||||
<TextBlock x:Name="TodayDayTextBlock"
|
<TextBlock x:Name="WeekdayText4" Text="四" HorizontalAlignment="Center" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" FontSize="13" FontWeight="SemiBold" />
|
||||||
Grid.Row="1"
|
<TextBlock x:Name="WeekdayText5" Text="五" HorizontalAlignment="Center" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" FontSize="13" FontWeight="SemiBold" />
|
||||||
HorizontalAlignment="Center"
|
<TextBlock x:Name="WeekdayText6" Text="六" HorizontalAlignment="Center" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" FontSize="13" FontWeight="SemiBold" />
|
||||||
VerticalAlignment="Center"
|
</UniformGrid>
|
||||||
FontSize="56"
|
|
||||||
FontWeight="Light"
|
<Grid x:Name="CalendarGrid"
|
||||||
Foreground="{DynamicResource AdaptiveOnAccentBrush}" />
|
Grid.Row="2"
|
||||||
|
RowDefinitions="*,*,*,*,*"
|
||||||
<!-- 星期 -->
|
ColumnDefinitions="*,*,*,*,*,*,*" />
|
||||||
<TextBlock x:Name="TodayWeekdayTextBlock"
|
|
||||||
Grid.Row="2"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
FontSize="13"
|
|
||||||
FontWeight="SemiBold"
|
|
||||||
Foreground="{DynamicResource AdaptiveOnAccentBrush}" />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
|
||||||
</Grid>
|
<Border x:Name="LunarCardBorder"
|
||||||
|
Grid.Column="1"
|
||||||
|
Background="{DynamicResource AdaptiveLayer2Brush}"
|
||||||
|
CornerRadius="24"
|
||||||
|
Padding="14">
|
||||||
|
<Grid x:Name="RightPanelGrid"
|
||||||
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto"
|
||||||
|
RowSpacing="10">
|
||||||
|
<TextBlock x:Name="LunarDateTextBlock"
|
||||||
|
Grid.Row="0"
|
||||||
|
FontSize="28"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||||
|
|
||||||
|
<TextBlock x:Name="LunarMetaTextBlock"
|
||||||
|
Grid.Row="1"
|
||||||
|
FontSize="14"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||||
|
Opacity="0.88"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
|
<Border Grid.Row="2"
|
||||||
|
Height="1"
|
||||||
|
Margin="0,2,0,2"
|
||||||
|
Background="{DynamicResource AdaptiveStrokeBrush}" />
|
||||||
|
|
||||||
|
<Grid Grid.Row="3"
|
||||||
|
ColumnDefinitions="Auto,*"
|
||||||
|
ColumnSpacing="8">
|
||||||
|
<TextBlock x:Name="YiLabelTextBlock"
|
||||||
|
Grid.Column="0"
|
||||||
|
Text="宜"
|
||||||
|
FontSize="18"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Foreground="#4E7D3A" />
|
||||||
|
<TextBlock x:Name="YiItemsTextBlock"
|
||||||
|
Grid.Column="1"
|
||||||
|
FontSize="16"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
TextTrimming="CharacterEllipsis" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid Grid.Row="4"
|
||||||
|
ColumnDefinitions="Auto,*"
|
||||||
|
ColumnSpacing="8">
|
||||||
|
<TextBlock x:Name="JiLabelTextBlock"
|
||||||
|
Grid.Column="0"
|
||||||
|
Text="忌"
|
||||||
|
FontSize="18"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Foreground="#A1473E" />
|
||||||
|
<TextBlock x:Name="JiItemsTextBlock"
|
||||||
|
Grid.Column="1"
|
||||||
|
FontSize="16"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
TextTrimming="CharacterEllipsis" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Viewbox>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
@@ -15,8 +15,15 @@ public partial class DateWidget : UserControl
|
|||||||
{
|
{
|
||||||
Interval = TimeSpan.FromMinutes(1)
|
Interval = TimeSpan.FromMinutes(1)
|
||||||
};
|
};
|
||||||
|
private static readonly LunarCalendarService LunarCalendarService = new();
|
||||||
|
|
||||||
|
private static readonly string[] ZhWeekdayHeaders = ["日", "一", "二", "三", "四", "五", "六"];
|
||||||
|
private static readonly string[] EnWeekdayHeaders = ["S", "M", "T", "W", "T", "F", "S"];
|
||||||
|
|
||||||
private TimeZoneService? _timeZoneService;
|
private TimeZoneService? _timeZoneService;
|
||||||
|
private double _currentCellSize = 64;
|
||||||
|
private double _calendarDayFontSize = 14;
|
||||||
|
private double _calendarTodayDotSize = 28;
|
||||||
|
|
||||||
public DateWidget()
|
public DateWidget()
|
||||||
{
|
{
|
||||||
@@ -25,12 +32,10 @@ public partial class DateWidget : UserControl
|
|||||||
_timer.Tick += OnTimerTick;
|
_timer.Tick += OnTimerTick;
|
||||||
AttachedToVisualTree += OnAttachedToVisualTree;
|
AttachedToVisualTree += OnAttachedToVisualTree;
|
||||||
DetachedFromVisualTree += OnDetachedFromVisualTree;
|
DetachedFromVisualTree += OnDetachedFromVisualTree;
|
||||||
|
SizeChanged += OnSizeChanged;
|
||||||
UpdateDate();
|
UpdateDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设置时区服务
|
|
||||||
/// </summary>
|
|
||||||
public void SetTimeZoneService(TimeZoneService timeZoneService)
|
public void SetTimeZoneService(TimeZoneService timeZoneService)
|
||||||
{
|
{
|
||||||
if (_timeZoneService != null)
|
if (_timeZoneService != null)
|
||||||
@@ -54,6 +59,11 @@ public partial class DateWidget : UserControl
|
|||||||
_timer.Stop();
|
_timer.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnSizeChanged(object? sender, SizeChangedEventArgs e)
|
||||||
|
{
|
||||||
|
ApplyCellSize(_currentCellSize);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnTimerTick(object? sender, EventArgs e)
|
private void OnTimerTick(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
UpdateDate();
|
UpdateDate();
|
||||||
@@ -68,30 +78,75 @@ public partial class DateWidget : UserControl
|
|||||||
{
|
{
|
||||||
var now = _timeZoneService?.GetCurrentTime() ?? DateTime.Now;
|
var now = _timeZoneService?.GetCurrentTime() ?? DateTime.Now;
|
||||||
var culture = CultureInfo.CurrentCulture;
|
var culture = CultureInfo.CurrentCulture;
|
||||||
|
var isZh = culture.TwoLetterISOLanguageName.Equals("zh", StringComparison.OrdinalIgnoreCase);
|
||||||
// 右侧:今日详情
|
var lunar = LunarCalendarService.GetLunarInfo(now);
|
||||||
TodayDayTextBlock.Text = now.Day.ToString();
|
|
||||||
TodayWeekdayTextBlock.Text = now.ToString("dddd", culture);
|
GregorianHeadlineTextBlock.Text = isZh
|
||||||
|
? $"{now.Month}月{now.Day}日 {ToChineseWeekday(now.DayOfWeek)}"
|
||||||
// 左侧:月历
|
: now.ToString("MMM d ddd", culture);
|
||||||
CalendarMonthYearTextBlock.Text = now.ToString("yyyy年M月", culture);
|
|
||||||
|
if (isZh)
|
||||||
// 生成月历
|
{
|
||||||
|
LunarDateTextBlock.Text = $"农历 {lunar.LunarDateZh}";
|
||||||
|
LunarMetaTextBlock.Text = $"{lunar.GanzhiYearZh}年({lunar.ZodiacZh}年)";
|
||||||
|
YiLabelTextBlock.Text = "宜";
|
||||||
|
JiLabelTextBlock.Text = "忌";
|
||||||
|
YiItemsTextBlock.Text = "祭祀 祈福 出行 会友";
|
||||||
|
JiItemsTextBlock.Text = "动土 诉讼 远航 争执";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LunarDateTextBlock.Text = $"Lunar {lunar.LunarDateEn}";
|
||||||
|
LunarMetaTextBlock.Text = $"Ganzhi year: {lunar.GanzhiYearEn} ({lunar.ZodiacEn})";
|
||||||
|
YiLabelTextBlock.Text = "Do";
|
||||||
|
JiLabelTextBlock.Text = "Avoid";
|
||||||
|
YiItemsTextBlock.Text = "Worship Blessing Travel Meet";
|
||||||
|
JiItemsTextBlock.Text = "Groundwork Lawsuit Voyage Dispute";
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateWeekdayHeaders(isZh);
|
||||||
GenerateCalendar(now);
|
GenerateCalendar(now);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string ToChineseWeekday(DayOfWeek dayOfWeek)
|
||||||
|
{
|
||||||
|
return dayOfWeek switch
|
||||||
|
{
|
||||||
|
DayOfWeek.Sunday => "周日",
|
||||||
|
DayOfWeek.Monday => "周一",
|
||||||
|
DayOfWeek.Tuesday => "周二",
|
||||||
|
DayOfWeek.Wednesday => "周三",
|
||||||
|
DayOfWeek.Thursday => "周四",
|
||||||
|
DayOfWeek.Friday => "周五",
|
||||||
|
_ => "周六"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateWeekdayHeaders(bool isZh)
|
||||||
|
{
|
||||||
|
var headers = isZh ? ZhWeekdayHeaders : EnWeekdayHeaders;
|
||||||
|
WeekdayText0.Text = headers[0];
|
||||||
|
WeekdayText1.Text = headers[1];
|
||||||
|
WeekdayText2.Text = headers[2];
|
||||||
|
WeekdayText3.Text = headers[3];
|
||||||
|
WeekdayText4.Text = headers[4];
|
||||||
|
WeekdayText5.Text = headers[5];
|
||||||
|
WeekdayText6.Text = headers[6];
|
||||||
|
}
|
||||||
|
|
||||||
private void GenerateCalendar(DateTime currentDate)
|
private void GenerateCalendar(DateTime currentDate)
|
||||||
{
|
{
|
||||||
// 清空之前的日期(保留星期标题)
|
var removeList = new List<Control>();
|
||||||
var childrenToRemove = new List<Control>();
|
|
||||||
foreach (var child in CalendarGrid.Children)
|
foreach (var child in CalendarGrid.Children)
|
||||||
{
|
{
|
||||||
if (child is TextBlock tb && tb.Tag?.ToString() == "day")
|
if (child is Control control && control.Tag is string tag &&
|
||||||
|
(tag == "day" || tag == "today-dot"))
|
||||||
{
|
{
|
||||||
childrenToRemove.Add(tb);
|
removeList.Add(control);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach (var child in childrenToRemove)
|
|
||||||
|
foreach (var child in removeList)
|
||||||
{
|
{
|
||||||
CalendarGrid.Children.Remove(child);
|
CalendarGrid.Children.Remove(child);
|
||||||
}
|
}
|
||||||
@@ -99,66 +154,62 @@ public partial class DateWidget : UserControl
|
|||||||
var year = currentDate.Year;
|
var year = currentDate.Year;
|
||||||
var month = currentDate.Month;
|
var month = currentDate.Month;
|
||||||
var today = currentDate.Day;
|
var today = currentDate.Day;
|
||||||
|
|
||||||
// 获取该月第一天
|
|
||||||
var firstDayOfMonth = new DateTime(year, month, 1);
|
var firstDayOfMonth = new DateTime(year, month, 1);
|
||||||
var daysInMonth = DateTime.DaysInMonth(year, month);
|
var daysInMonth = DateTime.DaysInMonth(year, month);
|
||||||
var startDayOfWeek = (int)firstDayOfMonth.DayOfWeek; // 0 = Sunday
|
var startDayOfWeek = (int)firstDayOfMonth.DayOfWeek;
|
||||||
|
|
||||||
// 生成日期
|
for (var day = 1; day <= daysInMonth; day++)
|
||||||
for (int day = 1; day <= daysInMonth; day++)
|
|
||||||
{
|
{
|
||||||
var row = ((day + startDayOfWeek - 1) / 7) + 1; // +1 because row 0 is weekday headers
|
var row = (day + startDayOfWeek - 1) / 7;
|
||||||
var col = (day + startDayOfWeek - 1) % 7;
|
var col = (day + startDayOfWeek - 1) % 7;
|
||||||
|
if (row > 4)
|
||||||
if (row > 5) continue; // 最多显示6行
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var dayText = new TextBlock
|
var dayText = new TextBlock
|
||||||
{
|
{
|
||||||
Text = day.ToString(),
|
Text = day.ToString(CultureInfo.CurrentCulture),
|
||||||
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
|
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
|
||||||
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
|
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
|
||||||
FontSize = 10,
|
FontSize = _calendarDayFontSize,
|
||||||
|
FontWeight = FontWeight.SemiBold,
|
||||||
Tag = "day"
|
Tag = "day"
|
||||||
};
|
};
|
||||||
|
|
||||||
// 今天高亮
|
|
||||||
if (day == today)
|
if (day == today)
|
||||||
{
|
{
|
||||||
// 使用主题色高亮今天
|
var accentBrush = this.TryFindResource("AdaptiveAccentBrush", out var accent)
|
||||||
var accentBrush = this.TryFindResource("AdaptiveAccentBrush", out var accent)
|
? accent as IBrush
|
||||||
? accent as IBrush
|
|
||||||
: Brushes.Blue;
|
: Brushes.Blue;
|
||||||
var onAccentBrush = this.TryFindResource("AdaptiveOnAccentBrush", out var onAccent)
|
var onAccentBrush = this.TryFindResource("AdaptiveOnAccentBrush", out var onAccent)
|
||||||
? onAccent as IBrush
|
? onAccent as IBrush
|
||||||
: Brushes.White;
|
: Brushes.White;
|
||||||
|
|
||||||
dayText.Foreground = onAccentBrush;
|
dayText.Foreground = onAccentBrush;
|
||||||
dayText.FontWeight = FontWeight.Bold;
|
var dot = new Border
|
||||||
dayText.Background = new SolidColorBrush(Colors.Transparent);
|
|
||||||
|
|
||||||
// 添加背景圆
|
|
||||||
var highlight = new Border
|
|
||||||
{
|
{
|
||||||
|
Width = _calendarTodayDotSize,
|
||||||
|
Height = _calendarTodayDotSize,
|
||||||
|
CornerRadius = new CornerRadius(_calendarTodayDotSize * 0.5),
|
||||||
Background = accentBrush,
|
Background = accentBrush,
|
||||||
CornerRadius = new CornerRadius(10),
|
|
||||||
Width = 20,
|
|
||||||
Height = 20,
|
|
||||||
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
|
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
|
||||||
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
|
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
|
||||||
Child = dayText
|
Child = dayText,
|
||||||
|
Tag = "today-dot"
|
||||||
};
|
};
|
||||||
Grid.SetRow(highlight, row);
|
|
||||||
Grid.SetColumn(highlight, col);
|
Grid.SetRow(dot, row);
|
||||||
CalendarGrid.Children.Add(highlight);
|
Grid.SetColumn(dot, col);
|
||||||
|
CalendarGrid.Children.Add(dot);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 使用主题次要文本颜色
|
var isWeekend = col is 0 or 6;
|
||||||
var secondaryBrush = this.TryFindResource("AdaptiveTextSecondaryBrush", out var secondary)
|
dayText.Foreground = isWeekend
|
||||||
? secondary as IBrush
|
? GetThemeBrush("AdaptiveTextSecondaryBrush", 0.82)
|
||||||
: Brushes.Gray;
|
: GetThemeBrush("AdaptiveTextPrimaryBrush", 0.92);
|
||||||
dayText.Foreground = secondaryBrush;
|
|
||||||
Grid.SetRow(dayText, row);
|
Grid.SetRow(dayText, row);
|
||||||
Grid.SetColumn(dayText, col);
|
Grid.SetColumn(dayText, col);
|
||||||
CalendarGrid.Children.Add(dayText);
|
CalendarGrid.Children.Add(dayText);
|
||||||
@@ -168,13 +219,59 @@ public partial class DateWidget : UserControl
|
|||||||
|
|
||||||
public void ApplyCellSize(double cellSize)
|
public void ApplyCellSize(double cellSize)
|
||||||
{
|
{
|
||||||
// 根据格子大小调整圆角
|
_currentCellSize = Math.Max(1, cellSize);
|
||||||
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(cellSize * 0.12, 8, 20));
|
var scale = ResolveScale();
|
||||||
|
|
||||||
// 调整字体大小
|
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(28 * scale, 16, 40));
|
||||||
var baseFontSize = cellSize * 0.25;
|
RootBorder.Padding = new Thickness(Math.Clamp(12 * scale, 8, 18));
|
||||||
TodayDayTextBlock.FontSize = Math.Clamp(baseFontSize * 2.8, 28, 72);
|
|
||||||
TodayWeekdayTextBlock.FontSize = Math.Clamp(baseFontSize * 0.6, 10, 16);
|
LeftPanelGrid.RowSpacing = Math.Clamp(8 * scale, 5, 14);
|
||||||
CalendarMonthYearTextBlock.FontSize = Math.Clamp(baseFontSize * 0.55, 9, 14);
|
RightPanelGrid.RowSpacing = Math.Clamp(10 * scale, 6, 16);
|
||||||
|
LunarCardBorder.CornerRadius = new CornerRadius(Math.Clamp(24 * scale, 14, 34));
|
||||||
|
LunarCardBorder.Padding = new Thickness(Math.Clamp(14 * scale, 9, 20));
|
||||||
|
|
||||||
|
GregorianHeadlineTextBlock.FontSize = Math.Clamp(22 * scale, 14, 34);
|
||||||
|
WeekdayText0.FontSize = Math.Clamp(13 * scale, 9, 18);
|
||||||
|
WeekdayText1.FontSize = WeekdayText0.FontSize;
|
||||||
|
WeekdayText2.FontSize = WeekdayText0.FontSize;
|
||||||
|
WeekdayText3.FontSize = WeekdayText0.FontSize;
|
||||||
|
WeekdayText4.FontSize = WeekdayText0.FontSize;
|
||||||
|
WeekdayText5.FontSize = WeekdayText0.FontSize;
|
||||||
|
WeekdayText6.FontSize = WeekdayText0.FontSize;
|
||||||
|
|
||||||
|
LunarDateTextBlock.FontSize = Math.Clamp(28 * scale, 17, 44);
|
||||||
|
LunarMetaTextBlock.FontSize = Math.Clamp(14 * scale, 10, 22);
|
||||||
|
YiLabelTextBlock.FontSize = Math.Clamp(18 * scale, 12, 28);
|
||||||
|
JiLabelTextBlock.FontSize = YiLabelTextBlock.FontSize;
|
||||||
|
YiItemsTextBlock.FontSize = Math.Clamp(16 * scale, 11, 24);
|
||||||
|
JiItemsTextBlock.FontSize = YiItemsTextBlock.FontSize;
|
||||||
|
|
||||||
|
_calendarDayFontSize = Math.Clamp(14 * scale, 9, 22);
|
||||||
|
_calendarTodayDotSize = Math.Clamp(28 * scale, 17, 38);
|
||||||
|
|
||||||
|
UpdateDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private double ResolveScale()
|
||||||
|
{
|
||||||
|
var cellScale = Math.Clamp(_currentCellSize / 48d, 0.72, 1.55);
|
||||||
|
var heightScale = Bounds.Height > 1 ? Math.Clamp(Bounds.Height / 220d, 0.65, 1.65) : 1;
|
||||||
|
var widthScale = Bounds.Width > 1 ? Math.Clamp(Bounds.Width / 460d, 0.65, 1.65) : 1;
|
||||||
|
return Math.Clamp(Math.Min(cellScale, Math.Min(heightScale, widthScale) * 1.08), 0.65, 1.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IBrush GetThemeBrush(string key, double opacity)
|
||||||
|
{
|
||||||
|
if (this.TryFindResource(key, out var value) && value is IBrush brush)
|
||||||
|
{
|
||||||
|
if (brush is ISolidColorBrush solid)
|
||||||
|
{
|
||||||
|
return new SolidColorBrush(solid.Color, opacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return brush;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SolidColorBrush(Colors.Gray, opacity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<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"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignWidth="400"
|
||||||
|
d:DesignHeight="300"
|
||||||
|
x:Class="LanMontainDesktop.Views.Components.DateWidgetSettingsWindow">
|
||||||
|
|
||||||
|
<Border Background="{DynamicResource AdaptiveBackgroundBrush}"
|
||||||
|
Padding="24">
|
||||||
|
<StackPanel Spacing="16">
|
||||||
|
<TextBlock Text="日历组件设置"
|
||||||
|
FontSize="20"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace LanMontainDesktop.Views.Components;
|
||||||
|
|
||||||
|
public partial class DateWidgetSettingsWindow : UserControl
|
||||||
|
{
|
||||||
|
public DateWidgetSettingsWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
87
LanMontainDesktop/Views/Components/LunarCalendarWidget.axaml
Normal file
87
LanMontainDesktop/Views/Components/LunarCalendarWidget.axaml
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
<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"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignWidth="300"
|
||||||
|
d:DesignHeight="300"
|
||||||
|
x:Class="LanMontainDesktop.Views.Components.LunarCalendarWidget">
|
||||||
|
|
||||||
|
<Border x:Name="RootBorder"
|
||||||
|
Background="#EFE6D9"
|
||||||
|
CornerRadius="30"
|
||||||
|
ClipToBounds="True"
|
||||||
|
Padding="16">
|
||||||
|
<Viewbox Stretch="Uniform">
|
||||||
|
<Grid x:Name="LayoutRoot"
|
||||||
|
Width="300"
|
||||||
|
Height="300"
|
||||||
|
RowDefinitions="Auto,Auto,Auto,*"
|
||||||
|
RowSpacing="10">
|
||||||
|
<TextBlock x:Name="GregorianLineTextBlock"
|
||||||
|
Grid.Row="0"
|
||||||
|
Text="10/9 Thu"
|
||||||
|
FontSize="24"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="#7A5A47"
|
||||||
|
HorizontalAlignment="Center" />
|
||||||
|
|
||||||
|
<TextBlock x:Name="LunarDateTextBlock"
|
||||||
|
Grid.Row="1"
|
||||||
|
Text="Lunar"
|
||||||
|
FontSize="88"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Foreground="#6B4936"
|
||||||
|
HorizontalAlignment="Center" />
|
||||||
|
|
||||||
|
<Border x:Name="DividerBorder"
|
||||||
|
Grid.Row="2"
|
||||||
|
Height="1"
|
||||||
|
Margin="8,8,8,2"
|
||||||
|
Background="#D2C6B7" />
|
||||||
|
|
||||||
|
<Grid x:Name="AuspiciousGrid"
|
||||||
|
Grid.Row="3"
|
||||||
|
RowDefinitions="Auto,Auto"
|
||||||
|
RowSpacing="12">
|
||||||
|
<Grid Grid.Row="0"
|
||||||
|
ColumnDefinitions="Auto,*"
|
||||||
|
ColumnSpacing="8">
|
||||||
|
<TextBlock x:Name="YiLabelTextBlock"
|
||||||
|
Grid.Column="0"
|
||||||
|
Text="Yi"
|
||||||
|
FontSize="30"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Foreground="#5F6D2E" />
|
||||||
|
<TextBlock x:Name="YiItemsTextBlock"
|
||||||
|
Grid.Column="1"
|
||||||
|
FontSize="24"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="#6B4936"
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
MaxLines="1" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid Grid.Row="1"
|
||||||
|
ColumnDefinitions="Auto,*"
|
||||||
|
ColumnSpacing="8">
|
||||||
|
<TextBlock x:Name="JiLabelTextBlock"
|
||||||
|
Grid.Column="0"
|
||||||
|
Text="Ji"
|
||||||
|
FontSize="30"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Foreground="#8A4A3A" />
|
||||||
|
<TextBlock x:Name="JiItemsTextBlock"
|
||||||
|
Grid.Column="1"
|
||||||
|
FontSize="24"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="#6B4936"
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
MaxLines="1" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Viewbox>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
||||||
|
|
||||||
239
LanMontainDesktop/Views/Components/LunarCalendarWidget.axaml.cs
Normal file
239
LanMontainDesktop/Views/Components/LunarCalendarWidget.axaml.cs
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
using LanMontainDesktop.Services;
|
||||||
|
|
||||||
|
namespace LanMontainDesktop.Views.Components;
|
||||||
|
|
||||||
|
public partial class LunarCalendarWidget : UserControl
|
||||||
|
{
|
||||||
|
private readonly DispatcherTimer _timer = new()
|
||||||
|
{
|
||||||
|
Interval = TimeSpan.FromMinutes(1)
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly LunarCalendarService LunarCalendarService = new();
|
||||||
|
|
||||||
|
private static readonly string[] ZhYiCandidates =
|
||||||
|
[
|
||||||
|
"\u796d\u7940",
|
||||||
|
"\u7948\u798f",
|
||||||
|
"\u4f1a\u53cb",
|
||||||
|
"\u51fa\u884c",
|
||||||
|
"\u6c42\u8d22",
|
||||||
|
"\u5f00\u5e02",
|
||||||
|
"\u4ea4\u6613",
|
||||||
|
"\u5ac1\u5a36",
|
||||||
|
"\u6c42\u5b66",
|
||||||
|
"\u4fee\u9020",
|
||||||
|
"\u5b89\u5e8a",
|
||||||
|
"\u7eb3\u91c7"
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly string[] ZhJiCandidates =
|
||||||
|
[
|
||||||
|
"\u52a8\u571f",
|
||||||
|
"\u8bc9\u8bbc",
|
||||||
|
"\u8fdc\u822a",
|
||||||
|
"\u4e89\u6267",
|
||||||
|
"\u7834\u571f",
|
||||||
|
"\u5b89\u846c",
|
||||||
|
"\u4f10\u6728",
|
||||||
|
"\u6398\u4e95",
|
||||||
|
"\u8fc1\u5f99",
|
||||||
|
"\u5f00\u4ed3",
|
||||||
|
"\u7f6e\u4ea7",
|
||||||
|
"\u5f00\u6e20"
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly string[] EnYiCandidates =
|
||||||
|
[
|
||||||
|
"Worship",
|
||||||
|
"Blessing",
|
||||||
|
"Travel",
|
||||||
|
"Meetings",
|
||||||
|
"Trade",
|
||||||
|
"Business",
|
||||||
|
"Study",
|
||||||
|
"Build",
|
||||||
|
"Gathering",
|
||||||
|
"Planning"
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly string[] EnJiCandidates =
|
||||||
|
[
|
||||||
|
"Dispute",
|
||||||
|
"Lawsuit",
|
||||||
|
"Major move",
|
||||||
|
"Groundwork",
|
||||||
|
"Burial",
|
||||||
|
"Long voyage",
|
||||||
|
"Contract rush",
|
||||||
|
"Risky purchase",
|
||||||
|
"Heavy repair",
|
||||||
|
"Conflict"
|
||||||
|
];
|
||||||
|
|
||||||
|
private TimeZoneService? _timeZoneService;
|
||||||
|
private double _currentCellSize = 48;
|
||||||
|
|
||||||
|
public LunarCalendarWidget()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
_timer.Tick += OnTimerTick;
|
||||||
|
AttachedToVisualTree += OnAttachedToVisualTree;
|
||||||
|
DetachedFromVisualTree += OnDetachedFromVisualTree;
|
||||||
|
SizeChanged += OnSizeChanged;
|
||||||
|
UpdateContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTimeZoneService(TimeZoneService timeZoneService)
|
||||||
|
{
|
||||||
|
if (_timeZoneService is not null)
|
||||||
|
{
|
||||||
|
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
_timeZoneService = timeZoneService;
|
||||||
|
_timeZoneService.TimeZoneChanged += OnTimeZoneChanged;
|
||||||
|
UpdateContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
UpdateContent();
|
||||||
|
_timer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
_timer.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSizeChanged(object? sender, SizeChangedEventArgs e)
|
||||||
|
{
|
||||||
|
ApplyCellSize(_currentCellSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTimerTick(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
UpdateContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTimeZoneChanged(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
UpdateContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateContent()
|
||||||
|
{
|
||||||
|
var now = _timeZoneService?.GetCurrentTime() ?? DateTime.Now;
|
||||||
|
var culture = CultureInfo.CurrentCulture;
|
||||||
|
var isZh = culture.TwoLetterISOLanguageName.Equals("zh", StringComparison.OrdinalIgnoreCase);
|
||||||
|
var lunar = LunarCalendarService.GetLunarInfo(now);
|
||||||
|
|
||||||
|
GregorianLineTextBlock.Text = isZh
|
||||||
|
? $"{now.Month}\u6708{now.Day}\u65e5 {ToChineseWeekday(now.DayOfWeek)}"
|
||||||
|
: now.ToString("MMM d ddd", culture);
|
||||||
|
|
||||||
|
LunarDateTextBlock.Text = isZh ? lunar.LunarDateZh : lunar.LunarDateEn;
|
||||||
|
YiLabelTextBlock.Text = isZh ? "\u5b9c" : "Do";
|
||||||
|
JiLabelTextBlock.Text = isZh ? "\u5fcc" : "Avoid";
|
||||||
|
YiItemsTextBlock.Text = BuildDailySelection(
|
||||||
|
now.Date,
|
||||||
|
isZh ? ZhYiCandidates : EnYiCandidates,
|
||||||
|
count: 4,
|
||||||
|
salt: 17,
|
||||||
|
useChineseSpacing: isZh);
|
||||||
|
JiItemsTextBlock.Text = BuildDailySelection(
|
||||||
|
now.Date,
|
||||||
|
isZh ? ZhJiCandidates : EnJiCandidates,
|
||||||
|
count: 4,
|
||||||
|
salt: 29,
|
||||||
|
useChineseSpacing: isZh);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyCellSize(double cellSize)
|
||||||
|
{
|
||||||
|
_currentCellSize = Math.Max(1, cellSize);
|
||||||
|
var scale = ResolveScale();
|
||||||
|
|
||||||
|
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(30 * scale, 16, 44));
|
||||||
|
RootBorder.Padding = new Thickness(Math.Clamp(16 * scale, 8, 24));
|
||||||
|
LayoutRoot.RowSpacing = Math.Clamp(10 * scale, 5, 18);
|
||||||
|
DividerBorder.Margin = new Thickness(
|
||||||
|
Math.Clamp(8 * scale, 3, 14),
|
||||||
|
Math.Clamp(8 * scale, 3, 14),
|
||||||
|
Math.Clamp(8 * scale, 3, 14),
|
||||||
|
Math.Clamp(2 * scale, 1, 6));
|
||||||
|
AuspiciousGrid.RowSpacing = Math.Clamp(12 * scale, 6, 20);
|
||||||
|
|
||||||
|
GregorianLineTextBlock.FontSize = Math.Clamp(24 * scale, 11, 36);
|
||||||
|
LunarDateTextBlock.FontSize = Math.Clamp(88 * scale, 30, 130);
|
||||||
|
YiLabelTextBlock.FontSize = Math.Clamp(30 * scale, 13, 44);
|
||||||
|
JiLabelTextBlock.FontSize = YiLabelTextBlock.FontSize;
|
||||||
|
YiItemsTextBlock.FontSize = Math.Clamp(24 * scale, 11, 36);
|
||||||
|
JiItemsTextBlock.FontSize = YiItemsTextBlock.FontSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double ResolveScale()
|
||||||
|
{
|
||||||
|
var cellScale = Math.Clamp(_currentCellSize / 44d, 0.62, 1.95);
|
||||||
|
var heightScale = Bounds.Height > 1 ? Math.Clamp(Bounds.Height / 300d, 0.58, 2.0) : 1;
|
||||||
|
var widthScale = Bounds.Width > 1 ? Math.Clamp(Bounds.Width / 300d, 0.58, 2.0) : 1;
|
||||||
|
return Math.Clamp(Math.Min(cellScale, Math.Min(heightScale, widthScale) * 1.05), 0.58, 1.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ToChineseWeekday(DayOfWeek dayOfWeek)
|
||||||
|
{
|
||||||
|
return dayOfWeek switch
|
||||||
|
{
|
||||||
|
DayOfWeek.Sunday => "\u5468\u65e5",
|
||||||
|
DayOfWeek.Monday => "\u5468\u4e00",
|
||||||
|
DayOfWeek.Tuesday => "\u5468\u4e8c",
|
||||||
|
DayOfWeek.Wednesday => "\u5468\u4e09",
|
||||||
|
DayOfWeek.Thursday => "\u5468\u56db",
|
||||||
|
DayOfWeek.Friday => "\u5468\u4e94",
|
||||||
|
_ => "\u5468\u516d"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildDailySelection(
|
||||||
|
DateTime date,
|
||||||
|
string[] pool,
|
||||||
|
int count,
|
||||||
|
int salt,
|
||||||
|
bool useChineseSpacing)
|
||||||
|
{
|
||||||
|
if (pool.Length == 0 || count <= 0)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var target = Math.Min(count, pool.Length);
|
||||||
|
var selected = new List<string>(target);
|
||||||
|
var usedIndices = new HashSet<int>();
|
||||||
|
var cursor = Math.Abs(date.Year * 1009 + date.DayOfYear * 37 + salt * 211);
|
||||||
|
var step = (salt % Math.Max(1, pool.Length - 1)) + 1;
|
||||||
|
|
||||||
|
for (var i = 0; i < pool.Length * 3 && selected.Count < target; i++)
|
||||||
|
{
|
||||||
|
var index = (cursor + i * step) % pool.Length;
|
||||||
|
if (usedIndices.Add(index))
|
||||||
|
{
|
||||||
|
selected.Add(pool[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected.Count == 0)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join(useChineseSpacing ? " " : ", ", selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
49
LanMontainDesktop/Views/Components/MonthCalendarWidget.axaml
Normal file
49
LanMontainDesktop/Views/Components/MonthCalendarWidget.axaml
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<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"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignWidth="280"
|
||||||
|
d:DesignHeight="280"
|
||||||
|
x:Class="LanMontainDesktop.Views.Components.MonthCalendarWidget">
|
||||||
|
|
||||||
|
<Border x:Name="RootBorder"
|
||||||
|
Background="{DynamicResource AdaptiveSurfaceRaisedBrush}"
|
||||||
|
BorderBrush="{DynamicResource AdaptiveButtonBorderBrush}"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="28"
|
||||||
|
ClipToBounds="True"
|
||||||
|
Padding="14">
|
||||||
|
<Viewbox Stretch="Uniform">
|
||||||
|
<Grid x:Name="LayoutRoot"
|
||||||
|
Width="280"
|
||||||
|
Height="280"
|
||||||
|
RowDefinitions="Auto,Auto,*"
|
||||||
|
RowSpacing="10">
|
||||||
|
|
||||||
|
<TextBlock x:Name="HeaderTextBlock"
|
||||||
|
Grid.Row="0"
|
||||||
|
FontSize="42"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||||
|
|
||||||
|
<UniformGrid Grid.Row="1"
|
||||||
|
Columns="7">
|
||||||
|
<TextBlock x:Name="WeekdayText0" Text="S" HorizontalAlignment="Center" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" FontSize="20" FontWeight="SemiBold" />
|
||||||
|
<TextBlock x:Name="WeekdayText1" Text="M" HorizontalAlignment="Center" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" FontSize="20" FontWeight="SemiBold" />
|
||||||
|
<TextBlock x:Name="WeekdayText2" Text="T" HorizontalAlignment="Center" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" FontSize="20" FontWeight="SemiBold" />
|
||||||
|
<TextBlock x:Name="WeekdayText3" Text="W" HorizontalAlignment="Center" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" FontSize="20" FontWeight="SemiBold" />
|
||||||
|
<TextBlock x:Name="WeekdayText4" Text="T" HorizontalAlignment="Center" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" FontSize="20" FontWeight="SemiBold" />
|
||||||
|
<TextBlock x:Name="WeekdayText5" Text="F" HorizontalAlignment="Center" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" FontSize="20" FontWeight="SemiBold" />
|
||||||
|
<TextBlock x:Name="WeekdayText6" Text="S" HorizontalAlignment="Center" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" FontSize="20" FontWeight="SemiBold" />
|
||||||
|
</UniformGrid>
|
||||||
|
|
||||||
|
<Grid x:Name="CalendarGrid"
|
||||||
|
Grid.Row="2"
|
||||||
|
RowDefinitions="*,*,*,*,*,*"
|
||||||
|
ColumnDefinitions="*,*,*,*,*,*,*" />
|
||||||
|
</Grid>
|
||||||
|
</Viewbox>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
||||||
|
|
||||||
244
LanMontainDesktop/Views/Components/MonthCalendarWidget.axaml.cs
Normal file
244
LanMontainDesktop/Views/Components/MonthCalendarWidget.axaml.cs
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
using LanMontainDesktop.Services;
|
||||||
|
|
||||||
|
namespace LanMontainDesktop.Views.Components;
|
||||||
|
|
||||||
|
public partial class MonthCalendarWidget : UserControl
|
||||||
|
{
|
||||||
|
private readonly DispatcherTimer _timer = new()
|
||||||
|
{
|
||||||
|
Interval = TimeSpan.FromMinutes(1)
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly string[] ZhWeekdayHeaders = ["\u65e5", "\u4e00", "\u4e8c", "\u4e09", "\u56db", "\u4e94", "\u516d"];
|
||||||
|
private static readonly string[] EnWeekdayHeaders = ["S", "M", "T", "W", "T", "F", "S"];
|
||||||
|
|
||||||
|
private TimeZoneService? _timeZoneService;
|
||||||
|
private double _currentCellSize = 48;
|
||||||
|
private double _calendarDayFontSize = 22;
|
||||||
|
private double _calendarTodayDotSize = 44;
|
||||||
|
|
||||||
|
public MonthCalendarWidget()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
_timer.Tick += OnTimerTick;
|
||||||
|
AttachedToVisualTree += OnAttachedToVisualTree;
|
||||||
|
DetachedFromVisualTree += OnDetachedFromVisualTree;
|
||||||
|
SizeChanged += OnSizeChanged;
|
||||||
|
UpdateCalendar();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTimeZoneService(TimeZoneService timeZoneService)
|
||||||
|
{
|
||||||
|
if (_timeZoneService is not null)
|
||||||
|
{
|
||||||
|
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
_timeZoneService = timeZoneService;
|
||||||
|
_timeZoneService.TimeZoneChanged += OnTimeZoneChanged;
|
||||||
|
UpdateCalendar();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
UpdateCalendar();
|
||||||
|
_timer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
_timer.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSizeChanged(object? sender, SizeChangedEventArgs e)
|
||||||
|
{
|
||||||
|
ApplyCellSize(_currentCellSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTimerTick(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
UpdateCalendar();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTimeZoneChanged(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
UpdateCalendar();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateCalendar()
|
||||||
|
{
|
||||||
|
var now = _timeZoneService?.GetCurrentTime() ?? DateTime.Now;
|
||||||
|
var culture = CultureInfo.CurrentCulture;
|
||||||
|
var isZh = culture.TwoLetterISOLanguageName.Equals("zh", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
HeaderTextBlock.Text = isZh
|
||||||
|
? $"{now.Month}\u6708{now.Day}\u65e5"
|
||||||
|
: now.ToString("MMM d", culture);
|
||||||
|
|
||||||
|
UpdateWeekdayHeaders(isZh);
|
||||||
|
GenerateCalendar(now);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateWeekdayHeaders(bool isZh)
|
||||||
|
{
|
||||||
|
var headers = isZh ? ZhWeekdayHeaders : EnWeekdayHeaders;
|
||||||
|
var blocks = GetWeekdayHeaderBlocks();
|
||||||
|
for (var i = 0; i < blocks.Count; i++)
|
||||||
|
{
|
||||||
|
blocks[i].Text = headers[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IReadOnlyList<TextBlock> GetWeekdayHeaderBlocks()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
[
|
||||||
|
WeekdayText0,
|
||||||
|
WeekdayText1,
|
||||||
|
WeekdayText2,
|
||||||
|
WeekdayText3,
|
||||||
|
WeekdayText4,
|
||||||
|
WeekdayText5,
|
||||||
|
WeekdayText6
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenerateCalendar(DateTime currentDate)
|
||||||
|
{
|
||||||
|
var removeList = new List<Control>();
|
||||||
|
foreach (var child in CalendarGrid.Children)
|
||||||
|
{
|
||||||
|
if (child is Control control &&
|
||||||
|
control.Tag is string tag &&
|
||||||
|
(tag == "day" || tag == "today-dot"))
|
||||||
|
{
|
||||||
|
removeList.Add(control);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var child in removeList)
|
||||||
|
{
|
||||||
|
CalendarGrid.Children.Remove(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
var year = currentDate.Year;
|
||||||
|
var month = currentDate.Month;
|
||||||
|
var today = currentDate.Day;
|
||||||
|
|
||||||
|
var firstDayOfMonth = new DateTime(year, month, 1);
|
||||||
|
var daysInMonth = DateTime.DaysInMonth(year, month);
|
||||||
|
var startDayOfWeek = (int)firstDayOfMonth.DayOfWeek;
|
||||||
|
|
||||||
|
for (var day = 1; day <= daysInMonth; day++)
|
||||||
|
{
|
||||||
|
var row = (day + startDayOfWeek - 1) / 7;
|
||||||
|
var col = (day + startDayOfWeek - 1) % 7;
|
||||||
|
if (row > 5)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dayText = new TextBlock
|
||||||
|
{
|
||||||
|
Text = day.ToString(CultureInfo.CurrentCulture),
|
||||||
|
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
|
||||||
|
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
|
||||||
|
FontSize = _calendarDayFontSize,
|
||||||
|
FontWeight = FontWeight.SemiBold,
|
||||||
|
Tag = "day"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (day == today)
|
||||||
|
{
|
||||||
|
var accentBrush = this.TryFindResource("AdaptiveAccentBrush", out var accent)
|
||||||
|
? accent as IBrush
|
||||||
|
: Brushes.Blue;
|
||||||
|
var onAccentBrush = this.TryFindResource("AdaptiveOnAccentBrush", out var onAccent)
|
||||||
|
? onAccent as IBrush
|
||||||
|
: Brushes.White;
|
||||||
|
|
||||||
|
dayText.Foreground = onAccentBrush;
|
||||||
|
var dot = new Border
|
||||||
|
{
|
||||||
|
Width = _calendarTodayDotSize,
|
||||||
|
Height = _calendarTodayDotSize,
|
||||||
|
CornerRadius = new CornerRadius(_calendarTodayDotSize * 0.5),
|
||||||
|
Background = accentBrush,
|
||||||
|
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
|
||||||
|
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
|
||||||
|
Child = dayText,
|
||||||
|
Tag = "today-dot"
|
||||||
|
};
|
||||||
|
|
||||||
|
Grid.SetRow(dot, row);
|
||||||
|
Grid.SetColumn(dot, col);
|
||||||
|
CalendarGrid.Children.Add(dot);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var isWeekend = col is 0 or 6;
|
||||||
|
dayText.Foreground = isWeekend
|
||||||
|
? GetThemeBrush("AdaptiveTextSecondaryBrush", 0.78)
|
||||||
|
: GetThemeBrush("AdaptiveTextPrimaryBrush", 0.94);
|
||||||
|
Grid.SetRow(dayText, row);
|
||||||
|
Grid.SetColumn(dayText, col);
|
||||||
|
CalendarGrid.Children.Add(dayText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyCellSize(double cellSize)
|
||||||
|
{
|
||||||
|
_currentCellSize = Math.Max(1, cellSize);
|
||||||
|
var scale = ResolveScale();
|
||||||
|
|
||||||
|
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(28 * scale, 14, 40));
|
||||||
|
RootBorder.Padding = new Thickness(Math.Clamp(14 * scale, 8, 22));
|
||||||
|
LayoutRoot.RowSpacing = Math.Clamp(10 * scale, 5, 16);
|
||||||
|
|
||||||
|
HeaderTextBlock.FontSize = Math.Clamp(42 * scale, 14, 58);
|
||||||
|
|
||||||
|
var weekdayFontSize = Math.Clamp(20 * scale, 8, 26);
|
||||||
|
foreach (var block in GetWeekdayHeaderBlocks())
|
||||||
|
{
|
||||||
|
block.FontSize = weekdayFontSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
_calendarDayFontSize = Math.Clamp(22 * scale, 8, 30);
|
||||||
|
_calendarTodayDotSize = Math.Clamp(44 * scale, 16, 58);
|
||||||
|
|
||||||
|
UpdateCalendar();
|
||||||
|
}
|
||||||
|
|
||||||
|
private double ResolveScale()
|
||||||
|
{
|
||||||
|
var cellScale = Math.Clamp(_currentCellSize / 44d, 0.65, 1.85);
|
||||||
|
var heightScale = Bounds.Height > 1 ? Math.Clamp(Bounds.Height / 280d, 0.60, 1.90) : 1;
|
||||||
|
var widthScale = Bounds.Width > 1 ? Math.Clamp(Bounds.Width / 280d, 0.60, 1.90) : 1;
|
||||||
|
return Math.Clamp(Math.Min(cellScale, Math.Min(heightScale, widthScale) * 1.06), 0.60, 1.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IBrush GetThemeBrush(string key, double opacity)
|
||||||
|
{
|
||||||
|
if (this.TryFindResource(key, out var value) && value is IBrush brush)
|
||||||
|
{
|
||||||
|
if (brush is ISolidColorBrush solid)
|
||||||
|
{
|
||||||
|
return new SolidColorBrush(solid.Color, opacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return brush;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SolidColorBrush(Colors.Gray, opacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -98,8 +98,10 @@ public partial class MainWindow
|
|||||||
|
|
||||||
var viewportRow = gridMetrics.RowCount > 2 ? 1 : 0;
|
var viewportRow = gridMetrics.RowCount > 2 ? 1 : 0;
|
||||||
var viewportRowSpan = gridMetrics.RowCount > 2 ? gridMetrics.RowCount - 2 : 1;
|
var viewportRowSpan = gridMetrics.RowCount > 2 ? gridMetrics.RowCount - 2 : 1;
|
||||||
var pageWidth = Math.Max(1, gridMetrics.ColumnCount * gridMetrics.CellSize);
|
var pageWidth = Math.Max(1, gridMetrics.GridWidthPx);
|
||||||
var pageHeight = Math.Max(1, viewportRowSpan * gridMetrics.CellSize);
|
var pageHeight = Math.Max(
|
||||||
|
1,
|
||||||
|
viewportRowSpan * gridMetrics.CellSize + Math.Max(0, viewportRowSpan - 1) * gridMetrics.GapPx);
|
||||||
|
|
||||||
Grid.SetRow(DesktopPagesViewport, viewportRow);
|
Grid.SetRow(DesktopPagesViewport, viewportRow);
|
||||||
Grid.SetColumn(DesktopPagesViewport, 0);
|
Grid.SetColumn(DesktopPagesViewport, 0);
|
||||||
@@ -137,6 +139,8 @@ public partial class MainWindow
|
|||||||
{
|
{
|
||||||
Width = pageWidth,
|
Width = pageWidth,
|
||||||
Height = pageHeight,
|
Height = pageHeight,
|
||||||
|
RowSpacing = gridMetrics.GapPx,
|
||||||
|
ColumnSpacing = gridMetrics.GapPx,
|
||||||
Background = Brushes.Transparent,
|
Background = Brushes.Transparent,
|
||||||
ShowGridLines = false
|
ShowGridLines = false
|
||||||
};
|
};
|
||||||
@@ -309,6 +313,16 @@ public partial class MainWindow
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果在组件编辑模式下点击空白区域,取消组件选中
|
||||||
|
if (_isComponentLibraryOpen && _selectedDesktopComponentHost is not null)
|
||||||
|
{
|
||||||
|
if (!IsInteractivePointerSource(e.Source))
|
||||||
|
{
|
||||||
|
ClearDesktopComponentSelection();
|
||||||
|
ApplyTaskbarActionVisibility(GetCurrentTaskbarContext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!CanSwipeDesktopSurface())
|
if (!CanSwipeDesktopSurface())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -519,7 +533,7 @@ public partial class MainWindow
|
|||||||
Classes = { "glass-panel" },
|
Classes = { "glass-panel" },
|
||||||
BorderThickness = new Thickness(0),
|
BorderThickness = new Thickness(0),
|
||||||
Margin = new Thickness(0, 0, 12, 12),
|
Margin = new Thickness(0, 0, 12, 12),
|
||||||
CornerRadius = new CornerRadius(12),
|
CornerRadius = new CornerRadius(20),
|
||||||
Child = panel
|
Child = panel
|
||||||
// 不设置固定 Width 和 Height,由 UpdateLauncherTileLayout 动态设置
|
// 不设置固定 Width 和 Height,由 UpdateLauncherTileLayout 动态设置
|
||||||
};
|
};
|
||||||
@@ -598,7 +612,7 @@ public partial class MainWindow
|
|||||||
Classes = { "glass-panel" },
|
Classes = { "glass-panel" },
|
||||||
Margin = new Thickness(0, 0, 12, 12),
|
Margin = new Thickness(0, 0, 12, 12),
|
||||||
BorderThickness = new Thickness(0),
|
BorderThickness = new Thickness(0),
|
||||||
CornerRadius = new CornerRadius(12),
|
CornerRadius = new CornerRadius(20),
|
||||||
Padding = new Thickness(10),
|
Padding = new Thickness(10),
|
||||||
Content = content
|
Content = content
|
||||||
// 不设置固定 Width 和 Height,由 UpdateLauncherTileLayout 动态设置
|
// 不设置固定 Width 和 Height,由 UpdateLauncherTileLayout 动态设置
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Layout;
|
||||||
|
using FluentIcons.Avalonia;
|
||||||
|
using FluentIcons.Common;
|
||||||
|
|
||||||
namespace LanMontainDesktop.Views;
|
namespace LanMontainDesktop.Views;
|
||||||
|
|
||||||
@@ -59,11 +62,11 @@ public partial class MainWindow
|
|||||||
WallpaperPreviewBackButtonTextBlock.Text = L("button.back_to_windows", "Back to Windows");
|
WallpaperPreviewBackButtonTextBlock.Text = L("button.back_to_windows", "Back to Windows");
|
||||||
ToolTip.SetTip(BackToWindowsButton, L("tooltip.back_to_windows", "Back to Windows"));
|
ToolTip.SetTip(BackToWindowsButton, L("tooltip.back_to_windows", "Back to Windows"));
|
||||||
|
|
||||||
OpenComponentLibraryTextBlock.Text = L("button.component_library", "Edit Desktop");
|
OpenComponentLibraryTextBlock.Text = L("button.component_library", "编辑桌面");
|
||||||
WallpaperPreviewComponentLibraryTextBlock.Text = L("button.component_library", "Edit Desktop");
|
WallpaperPreviewComponentLibraryTextBlock.Text = L("button.component_library", "编辑桌面");
|
||||||
GridPreviewComponentLibraryTextBlock.Text = L("button.component_library", "Edit Desktop");
|
GridPreviewComponentLibraryTextBlock.Text = L("button.component_library", "编辑桌面");
|
||||||
ToolTip.SetTip(OpenComponentLibraryButton, L("tooltip.component_library", "Edit Desktop"));
|
ToolTip.SetTip(OpenComponentLibraryButton, L("tooltip.component_library", "编辑桌面"));
|
||||||
ComponentLibraryTitleTextBlock.Text = L("component_library.title", "Edit Desktop");
|
ComponentLibraryTitleTextBlock.Text = L("component_library.title", "小组件");
|
||||||
ToolTip.SetTip(CloseComponentLibraryButton, L("common.close", "Close"));
|
ToolTip.SetTip(CloseComponentLibraryButton, L("common.close", "Close"));
|
||||||
ComponentLibraryEmptyTextBlock.Text = L(
|
ComponentLibraryEmptyTextBlock.Text = L(
|
||||||
"component_library.empty",
|
"component_library.empty",
|
||||||
@@ -88,17 +91,55 @@ public partial class MainWindow
|
|||||||
ClearWallpaperButton.Content = L("settings.wallpaper.clear_button", "重置");
|
ClearWallpaperButton.Content = L("settings.wallpaper.clear_button", "重置");
|
||||||
|
|
||||||
GridPanelTitleTextBlock.Text = L("settings.grid.title", "Grid Layout");
|
GridPanelTitleTextBlock.Text = L("settings.grid.title", "Grid Layout");
|
||||||
|
GridSpacingPresetLabelTextBlock.Text = L("settings.grid.spacing_label", "Grid Spacing");
|
||||||
|
GridSpacingRelaxedComboBoxItem.Content = L("settings.grid.spacing_relaxed", "Relaxed");
|
||||||
|
GridSpacingCompactComboBoxItem.Content = L("settings.grid.spacing_compact", "Compact");
|
||||||
|
GridEdgeInsetLabelTextBlock.Text = L("settings.grid.edge_inset_label", "Screen Inset");
|
||||||
|
ApplyGridButton.Content = L("settings.grid.apply_button", "Apply");
|
||||||
|
UpdateGridEdgeInsetComputedPxText(_currentDesktopCellSize);
|
||||||
|
|
||||||
ColorPanelTitleTextBlock.Text = L("settings.color.title", "Color");
|
ColorPanelTitleTextBlock.Text = L("settings.color.title", "Color");
|
||||||
ThemeModeSettingsExpander.Header = L("settings.color.day_night_label", "Day/Night");
|
ThemeModeSettingsExpander.Header = L("settings.color.day_night_label", "Day/Night");
|
||||||
NightModeToggleSwitch.OnContent = L("settings.color.day_night_on", "Night");
|
NightModeToggleSwitch.OffContent = new StackPanel
|
||||||
NightModeToggleSwitch.OffContent = L("settings.color.day_night_off", "Day");
|
{
|
||||||
|
Orientation = Orientation.Horizontal,
|
||||||
|
Spacing = 6,
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
new SymbolIcon { Symbol = Symbol.WeatherSunny, IconVariant = IconVariant.Regular, FontSize = 14 },
|
||||||
|
new TextBlock
|
||||||
|
{
|
||||||
|
Text = L("settings.color.day_night_off", "Day"),
|
||||||
|
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
NightModeToggleSwitch.OnContent = new StackPanel
|
||||||
|
{
|
||||||
|
Orientation = Orientation.Horizontal,
|
||||||
|
Spacing = 6,
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
new SymbolIcon { Symbol = Symbol.WeatherMoon, IconVariant = IconVariant.Regular, FontSize = 14 },
|
||||||
|
new TextBlock
|
||||||
|
{
|
||||||
|
Text = L("settings.color.day_night_on", "Night"),
|
||||||
|
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
RecommendedColorsLabelTextBlock.Text = L("settings.color.recommended_label", "Recommended Colors");
|
RecommendedColorsLabelTextBlock.Text = L("settings.color.recommended_label", "Recommended Colors");
|
||||||
SystemMonetColorsLabelTextBlock.Text = L("settings.color.system_monet_label", "System Monet Colors");
|
SystemMonetColorsLabelTextBlock.Text = L("settings.color.system_monet_label", "System Monet Colors");
|
||||||
RefreshMonetColorsButton.Content = L("settings.color.refresh_button", "Refresh");
|
RefreshMonetColorsButton.Content = L("settings.color.refresh_button", "Refresh");
|
||||||
|
|
||||||
StatusBarPanelTitleTextBlock.Text = L("settings.status_bar.title", "Status Bar");
|
StatusBarPanelTitleTextBlock.Text = L("settings.status_bar.title", "Status Bar");
|
||||||
StatusBarClockSettingsExpander.Header = L("settings.status_bar.clock_header", "Clock");
|
StatusBarClockSettingsExpander.Header = L("settings.status_bar.clock_header", "Clock");
|
||||||
|
StatusBarSpacingSettingsExpander.Header = L("settings.status_bar.spacing_header", "Component Spacing");
|
||||||
|
StatusBarSpacingSettingsExpander.Description = L("settings.status_bar.spacing_desc", "Adjust spacing between status bar components.");
|
||||||
|
StatusBarSpacingModeCompactItem.Content = L("settings.status_bar.spacing_mode_compact", "Compact");
|
||||||
|
StatusBarSpacingModeRelaxedItem.Content = L("settings.status_bar.spacing_mode_relaxed", "Relaxed");
|
||||||
|
StatusBarSpacingModeCustomItem.Content = L("settings.status_bar.spacing_mode_custom", "Custom");
|
||||||
|
StatusBarSpacingCustomLabelTextBlock.Text = L("settings.status_bar.spacing_custom_label", "Custom spacing (%)");
|
||||||
|
|
||||||
RegionPanelTitleTextBlock.Text = L("settings.region.title", "Region");
|
RegionPanelTitleTextBlock.Text = L("settings.region.title", "Region");
|
||||||
LanguageSettingsExpander.Header = L("settings.region.language_header", "Language");
|
LanguageSettingsExpander.Header = L("settings.region.language_header", "Language");
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using FluentIcons.Avalonia;
|
using FluentIcons.Avalonia;
|
||||||
using FluentIcons.Common;
|
using FluentIcons.Common;
|
||||||
|
using LanMontainDesktop.Views.Components;
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -60,7 +61,8 @@ public partial class MainWindow
|
|||||||
WallpaperSettingsPanel is null ||
|
WallpaperSettingsPanel is null ||
|
||||||
ColorSettingsPanel is null ||
|
ColorSettingsPanel is null ||
|
||||||
StatusBarSettingsPanel is null ||
|
StatusBarSettingsPanel is null ||
|
||||||
RegionSettingsPanel is null)
|
RegionSettingsPanel is null ||
|
||||||
|
AboutSettingsPanel is null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -71,6 +73,7 @@ public partial class MainWindow
|
|||||||
ColorSettingsPanel.IsVisible = selectedIndex == 2;
|
ColorSettingsPanel.IsVisible = selectedIndex == 2;
|
||||||
StatusBarSettingsPanel.IsVisible = selectedIndex == 3;
|
StatusBarSettingsPanel.IsVisible = selectedIndex == 3;
|
||||||
RegionSettingsPanel.IsVisible = selectedIndex == 4;
|
RegionSettingsPanel.IsVisible = selectedIndex == 4;
|
||||||
|
AboutSettingsPanel.IsVisible = selectedIndex == 5;
|
||||||
|
|
||||||
if (selectedIndex == 1)
|
if (selectedIndex == 1)
|
||||||
{
|
{
|
||||||
@@ -633,6 +636,8 @@ public partial class MainWindow
|
|||||||
var snapshot = new AppSettingsSnapshot
|
var snapshot = new AppSettingsSnapshot
|
||||||
{
|
{
|
||||||
GridShortSideCells = _targetShortSideCells,
|
GridShortSideCells = _targetShortSideCells,
|
||||||
|
GridSpacingPreset = _gridSpacingPreset,
|
||||||
|
DesktopEdgeInsetPercent = _desktopEdgeInsetPercent,
|
||||||
IsNightMode = _isNightMode,
|
IsNightMode = _isNightMode,
|
||||||
ThemeColor = _selectedThemeColor.ToString(),
|
ThemeColor = _selectedThemeColor.ToString(),
|
||||||
WallpaperPath = _wallpaperPath,
|
WallpaperPath = _wallpaperPath,
|
||||||
@@ -644,6 +649,9 @@ public partial class MainWindow
|
|||||||
PinnedTaskbarActions = _pinnedTaskbarActions.Select(action => action.ToString()).ToList(),
|
PinnedTaskbarActions = _pinnedTaskbarActions.Select(action => action.ToString()).ToList(),
|
||||||
EnableDynamicTaskbarActions = _enableDynamicTaskbarActions,
|
EnableDynamicTaskbarActions = _enableDynamicTaskbarActions,
|
||||||
TaskbarLayoutMode = _taskbarLayoutMode,
|
TaskbarLayoutMode = _taskbarLayoutMode,
|
||||||
|
ClockDisplayFormat = _clockDisplayFormat == ClockDisplayFormat.HourMinute ? "HourMinute" : "HourMinuteSecond",
|
||||||
|
StatusBarSpacingMode = _statusBarSpacingMode,
|
||||||
|
StatusBarCustomSpacingPercent = _statusBarCustomSpacingPercent,
|
||||||
DesktopPageCount = _desktopPageCount,
|
DesktopPageCount = _desktopPageCount,
|
||||||
CurrentDesktopSurfaceIndex = _currentDesktopSurfaceIndex,
|
CurrentDesktopSurfaceIndex = _currentDesktopSurfaceIndex,
|
||||||
DesktopComponentPlacements = _desktopComponentPlacements.ToList()
|
DesktopComponentPlacements = _desktopComponentPlacements.ToList()
|
||||||
@@ -652,6 +660,23 @@ public partial class MainWindow
|
|||||||
_appSettingsService.Save(snapshot);
|
_appSettingsService.Save(snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IDisposable? _persistSettingsDebounceTimer;
|
||||||
|
|
||||||
|
private void SchedulePersistSettings(int delayMs = 200)
|
||||||
|
{
|
||||||
|
if (_suppressSettingsPersistence)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_persistSettingsDebounceTimer?.Dispose();
|
||||||
|
_persistSettingsDebounceTimer = DispatcherTimer.RunOnce(() =>
|
||||||
|
{
|
||||||
|
_persistSettingsDebounceTimer = null;
|
||||||
|
PersistSettings();
|
||||||
|
}, TimeSpan.FromMilliseconds(Math.Max(0, delayMs)));
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateAdaptiveTextSystem()
|
private void UpdateAdaptiveTextSystem()
|
||||||
{
|
{
|
||||||
var isLightBackground = _isSettingsOpen
|
var isLightBackground = _isSettingsOpen
|
||||||
@@ -980,8 +1005,13 @@ public partial class MainWindow
|
|||||||
UpdateAdaptiveTextSystem();
|
UpdateAdaptiveTextSystem();
|
||||||
ApplyWallpaperBrush();
|
ApplyWallpaperBrush();
|
||||||
ApplyTaskbarActionVisibility(GetCurrentTaskbarContext());
|
ApplyTaskbarActionVisibility(GetCurrentTaskbarContext());
|
||||||
|
if (_settingsContentPanelTransform is not null)
|
||||||
|
{
|
||||||
|
_settingsContentPanelTransform.Y = 30;
|
||||||
|
}
|
||||||
SettingsPage.IsVisible = true;
|
SettingsPage.IsVisible = true;
|
||||||
SettingsPage.Opacity = 0;
|
SettingsPage.Opacity = 0;
|
||||||
|
UpdateSettingsViewportInsets(Math.Max(1, _currentDesktopCellSize));
|
||||||
|
|
||||||
UpdateWallpaperPreviewLayout();
|
UpdateWallpaperPreviewLayout();
|
||||||
|
|
||||||
@@ -992,6 +1022,10 @@ public partial class MainWindow
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_settingsContentPanelTransform is not null)
|
||||||
|
{
|
||||||
|
_settingsContentPanelTransform.Y = 0;
|
||||||
|
}
|
||||||
SettingsPage.Opacity = 1;
|
SettingsPage.Opacity = 1;
|
||||||
}, DispatcherPriority.Background);
|
}, DispatcherPriority.Background);
|
||||||
}
|
}
|
||||||
@@ -1011,10 +1045,18 @@ public partial class MainWindow
|
|||||||
if (immediate)
|
if (immediate)
|
||||||
{
|
{
|
||||||
SettingsPage.Opacity = 0;
|
SettingsPage.Opacity = 0;
|
||||||
|
if (_settingsContentPanelTransform is not null)
|
||||||
|
{
|
||||||
|
_settingsContentPanelTransform.Y = 30;
|
||||||
|
}
|
||||||
SettingsPage.IsVisible = false;
|
SettingsPage.IsVisible = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_settingsContentPanelTransform is not null)
|
||||||
|
{
|
||||||
|
_settingsContentPanelTransform.Y = 30;
|
||||||
|
}
|
||||||
SettingsPage.Opacity = 0;
|
SettingsPage.Opacity = 0;
|
||||||
|
|
||||||
DispatcherTimer.RunOnce(() =>
|
DispatcherTimer.RunOnce(() =>
|
||||||
@@ -1059,6 +1101,15 @@ public partial class MainWindow
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StatusBarSpacingSettingsExpander is not null)
|
||||||
|
{
|
||||||
|
StatusBarSpacingSettingsExpander.IconSource = new FluentIcons.Avalonia.Fluent.SymbolIconSource
|
||||||
|
{
|
||||||
|
Symbol = Symbol.TextLineSpacing,
|
||||||
|
IconVariant = variant
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (LanguageSettingsExpander is not null)
|
if (LanguageSettingsExpander is not null)
|
||||||
{
|
{
|
||||||
LanguageSettingsExpander.IconSource = new FluentIcons.Avalonia.Fluent.SymbolIconSource
|
LanguageSettingsExpander.IconSource = new FluentIcons.Avalonia.Fluent.SymbolIconSource
|
||||||
|
|||||||
@@ -126,7 +126,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Classes="glass-panel"
|
Classes="glass-panel"
|
||||||
ClipToBounds="False"
|
ClipToBounds="False"
|
||||||
CornerRadius="18"
|
CornerRadius="36"
|
||||||
Padding="18">
|
Padding="18">
|
||||||
<Grid RowDefinitions="Auto,*">
|
<Grid RowDefinitions="Auto,*">
|
||||||
<StackPanel Spacing="4">
|
<StackPanel Spacing="4">
|
||||||
@@ -160,7 +160,7 @@
|
|||||||
Margin="52"
|
Margin="52"
|
||||||
MaxWidth="760"
|
MaxWidth="760"
|
||||||
MaxHeight="520"
|
MaxHeight="520"
|
||||||
CornerRadius="18"
|
CornerRadius="36"
|
||||||
Padding="14">
|
Padding="14">
|
||||||
<Border.RenderTransform>
|
<Border.RenderTransform>
|
||||||
<TranslateTransform Y="42" />
|
<TranslateTransform Y="42" />
|
||||||
@@ -232,12 +232,13 @@
|
|||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<Border x:Name="BottomTaskbarContainer"
|
<Border x:Name="BottomTaskbarContainer"
|
||||||
Classes="glass-strong"
|
Classes="glass-island"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.ColumnSpan="1"
|
Grid.ColumnSpan="1"
|
||||||
Margin="4"
|
HorizontalAlignment="Stretch"
|
||||||
CornerRadius="18"
|
Margin="0"
|
||||||
|
CornerRadius="36"
|
||||||
Padding="6">
|
Padding="6">
|
||||||
<Grid ColumnDefinitions="Auto,*,Auto"
|
<Grid ColumnDefinitions="Auto,*,Auto"
|
||||||
ColumnSpacing="8">
|
ColumnSpacing="8">
|
||||||
@@ -374,14 +375,14 @@
|
|||||||
</Border.RenderTransform>
|
</Border.RenderTransform>
|
||||||
|
|
||||||
<Border Classes="mica-strong"
|
<Border Classes="mica-strong"
|
||||||
CornerRadius="14"
|
CornerRadius="32"
|
||||||
Padding="18">
|
Padding="18">
|
||||||
<Grid ColumnDefinitions="220,*"
|
<Grid ColumnDefinitions="220,*"
|
||||||
ColumnSpacing="16">
|
ColumnSpacing="16">
|
||||||
<Border x:Name="SettingsNavPanelBorder"
|
<Border x:Name="SettingsNavPanelBorder"
|
||||||
Classes="glass-panel"
|
Classes="glass-panel"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
CornerRadius="10"
|
CornerRadius="28"
|
||||||
Padding="10">
|
Padding="10">
|
||||||
<Border.Styles>
|
<Border.Styles>
|
||||||
<Style Selector="ListBox#SettingsNavListBox">
|
<Style Selector="ListBox#SettingsNavListBox">
|
||||||
@@ -394,7 +395,7 @@
|
|||||||
<Setter Property="BorderThickness" Value="0" />
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
<Setter Property="Padding" Value="10,8" />
|
<Setter Property="Padding" Value="10,8" />
|
||||||
<Setter Property="Margin" Value="0,2" />
|
<Setter Property="Margin" Value="0,2" />
|
||||||
<Setter Property="CornerRadius" Value="8" />
|
<Setter Property="CornerRadius" Value="12" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="ListBox#SettingsNavListBox ListBoxItem:pointerover">
|
<Style Selector="ListBox#SettingsNavListBox ListBoxItem:pointerover">
|
||||||
<Setter Property="Background" Value="{DynamicResource AdaptiveNavItemHoverBackgroundBrush}" />
|
<Setter Property="Background" Value="{DynamicResource AdaptiveNavItemHoverBackgroundBrush}" />
|
||||||
@@ -443,13 +444,19 @@
|
|||||||
<TextBlock x:Name="SettingsNavRegionTextBlock" Text="地区" VerticalAlignment="Center" />
|
<TextBlock x:Name="SettingsNavRegionTextBlock" Text="地区" VerticalAlignment="Center" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ListBoxItem>
|
</ListBoxItem>
|
||||||
|
<ListBoxItem x:Name="SettingsNavAboutItem" ToolTip.Tip="关于">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||||
|
<fi:SymbolIcon x:Name="SettingsNavAboutIcon" Symbol="Info" IconVariant="Regular" />
|
||||||
|
<TextBlock x:Name="SettingsNavAboutTextBlock" Text="关于" VerticalAlignment="Center" />
|
||||||
|
</StackPanel>
|
||||||
|
</ListBoxItem>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<Border Grid.Column="1"
|
<Border Grid.Column="1"
|
||||||
Classes="glass-panel"
|
Classes="glass-panel"
|
||||||
CornerRadius="10"
|
CornerRadius="20"
|
||||||
Padding="14">
|
Padding="14">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid x:Name="WallpaperSettingsPanel"
|
<Grid x:Name="WallpaperSettingsPanel"
|
||||||
@@ -468,17 +475,17 @@
|
|||||||
<Border x:Name="WallpaperPreviewHost"
|
<Border x:Name="WallpaperPreviewHost"
|
||||||
Grid.Row="1" Grid.Column="0"
|
Grid.Row="1" Grid.Column="0"
|
||||||
Margin="0,0,16,0"
|
Margin="0,0,16,0"
|
||||||
VerticalAlignment="Top"
|
VerticalAlignment="Stretch"
|
||||||
HorizontalAlignment="Stretch">
|
HorizontalAlignment="Stretch">
|
||||||
<!-- Monitor Frame (Bezel) -->
|
<!-- Monitor Frame (Bezel) -->
|
||||||
<Border x:Name="WallpaperPreviewFrame"
|
<Border x:Name="WallpaperPreviewFrame"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
CornerRadius="14"
|
CornerRadius="28"
|
||||||
Background="#FF1A1A1A"
|
Background="#FF1A1A1A"
|
||||||
Padding="12">
|
Padding="12">
|
||||||
<Border x:Name="WallpaperPreviewViewport"
|
<Border x:Name="WallpaperPreviewViewport"
|
||||||
ClipToBounds="True"
|
ClipToBounds="True"
|
||||||
CornerRadius="4"
|
CornerRadius="12"
|
||||||
Background="#30111827">
|
Background="#30111827">
|
||||||
<Grid>
|
<Grid>
|
||||||
<vlc:VideoView x:Name="WallpaperPreviewVideoView"
|
<vlc:VideoView x:Name="WallpaperPreviewVideoView"
|
||||||
@@ -497,13 +504,8 @@
|
|||||||
<StackPanel x:Name="WallpaperPreviewTopStatusComponentsPanel"
|
<StackPanel x:Name="WallpaperPreviewTopStatusComponentsPanel"
|
||||||
Orientation="Horizontal"
|
Orientation="Horizontal"
|
||||||
Spacing="3">
|
Spacing="3">
|
||||||
<Border x:Name="WallpaperPreviewClockContainer"
|
<comp:ClockWidget x:Name="WallpaperPreviewClockWidget"
|
||||||
IsVisible="False">
|
IsVisible="False" />
|
||||||
<TextBlock x:Name="WallpaperPreviewClockTextBlock"
|
|
||||||
FontWeight="SemiBold"
|
|
||||||
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
|
||||||
Text="12:34" />
|
|
||||||
</Border>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
@@ -511,7 +513,7 @@
|
|||||||
Classes="glass-strong"
|
Classes="glass-strong"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Margin="3"
|
Margin="3"
|
||||||
CornerRadius="8"
|
CornerRadius="16"
|
||||||
Padding="2">
|
Padding="2">
|
||||||
<Grid ColumnDefinitions="Auto,*,Auto"
|
<Grid ColumnDefinitions="Auto,*,Auto"
|
||||||
ColumnSpacing="3">
|
ColumnSpacing="3">
|
||||||
@@ -619,15 +621,18 @@
|
|||||||
HorizontalAlignment="Stretch">
|
HorizontalAlignment="Stretch">
|
||||||
<Border x:Name="GridPreviewFrame"
|
<Border x:Name="GridPreviewFrame"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
CornerRadius="14"
|
CornerRadius="28"
|
||||||
Background="#FF1A1A1A"
|
Background="#FF1A1A1A"
|
||||||
Padding="12">
|
Padding="12">
|
||||||
<Border x:Name="GridPreviewViewport"
|
<Border x:Name="GridPreviewViewport"
|
||||||
ClipToBounds="True"
|
ClipToBounds="True"
|
||||||
CornerRadius="4"
|
CornerRadius="16"
|
||||||
Background="#30111827">
|
Background="#30111827">
|
||||||
<Panel>
|
<Panel>
|
||||||
<Canvas x:Name="GridPreviewLinesCanvas" />
|
<Canvas x:Name="GridPreviewLinesCanvas"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
IsHitTestVisible="False" />
|
||||||
<Grid x:Name="GridPreviewGrid"
|
<Grid x:Name="GridPreviewGrid"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center">
|
VerticalAlignment="Center">
|
||||||
@@ -645,7 +650,7 @@
|
|||||||
Classes="glass-strong"
|
Classes="glass-strong"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Margin="3"
|
Margin="3"
|
||||||
CornerRadius="8"
|
CornerRadius="16"
|
||||||
Padding="2">
|
Padding="2">
|
||||||
<Grid ColumnDefinitions="Auto,*,Auto"
|
<Grid ColumnDefinitions="Auto,*,Auto"
|
||||||
ColumnSpacing="3">
|
ColumnSpacing="3">
|
||||||
@@ -683,29 +688,70 @@
|
|||||||
Spacing="16">
|
Spacing="16">
|
||||||
<TextBlock Text="竖排格数" FontSize="16" FontWeight="SemiBold" Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
<TextBlock Text="竖排格数" FontSize="16" FontWeight="SemiBold" Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||||
|
|
||||||
|
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="12">
|
||||||
|
<Slider x:Name="GridSizeSlider"
|
||||||
|
Grid.Column="0"
|
||||||
|
Minimum="6"
|
||||||
|
Maximum="96"
|
||||||
|
TickFrequency="1"
|
||||||
|
TickPlacement="None"
|
||||||
|
Value="12"
|
||||||
|
ValueChanged="OnGridSizeSliderChanged" />
|
||||||
|
<ui:NumberBox x:Name="GridSizeNumberBox"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="80"
|
||||||
|
Minimum="6"
|
||||||
|
Maximum="96"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||||
|
Value="12" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<TextBlock x:Name="GridSpacingPresetLabelTextBlock"
|
||||||
|
FontSize="16"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||||
|
Text="Spacing" />
|
||||||
|
|
||||||
|
<ComboBox x:Name="GridSpacingPresetComboBox"
|
||||||
|
Width="220"
|
||||||
|
SelectionChanged="OnGridSpacingPresetSelectionChanged">
|
||||||
|
<ComboBoxItem x:Name="GridSpacingRelaxedComboBoxItem" Tag="Relaxed" Content="Relaxed" />
|
||||||
|
<ComboBoxItem x:Name="GridSpacingCompactComboBoxItem" Tag="Compact" Content="Compact" />
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
|
<TextBlock x:Name="GridEdgeInsetLabelTextBlock"
|
||||||
|
FontSize="16"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||||
|
Text="Screen Inset" />
|
||||||
|
|
||||||
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="12">
|
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="12">
|
||||||
<Slider x:Name="GridSizeSlider"
|
<Slider x:Name="GridEdgeInsetSlider"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Minimum="6"
|
Minimum="0"
|
||||||
Maximum="96"
|
Maximum="30"
|
||||||
TickFrequency="1"
|
TickFrequency="1"
|
||||||
TickPlacement="None"
|
TickPlacement="None"
|
||||||
Value="12"
|
Value="18"
|
||||||
ValueChanged="OnGridSizeSliderChanged" />
|
ValueChanged="OnGridEdgeInsetSliderChanged" />
|
||||||
<ui:NumberBox x:Name="GridSizeNumberBox"
|
<ui:NumberBox x:Name="GridEdgeInsetNumberBox"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="80"
|
Width="80"
|
||||||
Minimum="6"
|
Minimum="0"
|
||||||
Maximum="96"
|
Maximum="30"
|
||||||
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||||
Value="12" />
|
Value="18" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Button x:Name="ApplyGridButton"
|
<TextBlock x:Name="GridEdgeInsetComputedPxTextBlock"
|
||||||
HorizontalAlignment="Stretch"
|
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"
|
||||||
Padding="0,10"
|
Text="≈ 0 px" />
|
||||||
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
|
||||||
Click="OnApplyGridSizeClick"
|
<Button x:Name="ApplyGridButton"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Padding="0,10"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||||
|
Click="OnApplyGridSizeClick"
|
||||||
Content="应用" />
|
Content="应用" />
|
||||||
|
|
||||||
<TextBlock x:Name="GridInfoTextBlock"
|
<TextBlock x:Name="GridInfoTextBlock"
|
||||||
@@ -762,7 +808,7 @@
|
|||||||
<Border x:Name="RecommendedColorSwatch1"
|
<Border x:Name="RecommendedColorSwatch1"
|
||||||
Width="26"
|
Width="26"
|
||||||
Height="26"
|
Height="26"
|
||||||
CornerRadius="6"
|
CornerRadius="12"
|
||||||
BorderThickness="0" />
|
BorderThickness="0" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button x:Name="RecommendedColorButton2"
|
<Button x:Name="RecommendedColorButton2"
|
||||||
@@ -773,7 +819,7 @@
|
|||||||
<Border x:Name="RecommendedColorSwatch2"
|
<Border x:Name="RecommendedColorSwatch2"
|
||||||
Width="26"
|
Width="26"
|
||||||
Height="26"
|
Height="26"
|
||||||
CornerRadius="6"
|
CornerRadius="12"
|
||||||
BorderThickness="0" />
|
BorderThickness="0" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button x:Name="RecommendedColorButton3"
|
<Button x:Name="RecommendedColorButton3"
|
||||||
@@ -784,7 +830,7 @@
|
|||||||
<Border x:Name="RecommendedColorSwatch3"
|
<Border x:Name="RecommendedColorSwatch3"
|
||||||
Width="26"
|
Width="26"
|
||||||
Height="26"
|
Height="26"
|
||||||
CornerRadius="6"
|
CornerRadius="12"
|
||||||
BorderThickness="0" />
|
BorderThickness="0" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button x:Name="RecommendedColorButton4"
|
<Button x:Name="RecommendedColorButton4"
|
||||||
@@ -795,7 +841,7 @@
|
|||||||
<Border x:Name="RecommendedColorSwatch4"
|
<Border x:Name="RecommendedColorSwatch4"
|
||||||
Width="26"
|
Width="26"
|
||||||
Height="26"
|
Height="26"
|
||||||
CornerRadius="6"
|
CornerRadius="12"
|
||||||
BorderThickness="0" />
|
BorderThickness="0" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button x:Name="RecommendedColorButton5"
|
<Button x:Name="RecommendedColorButton5"
|
||||||
@@ -806,7 +852,7 @@
|
|||||||
<Border x:Name="RecommendedColorSwatch5"
|
<Border x:Name="RecommendedColorSwatch5"
|
||||||
Width="26"
|
Width="26"
|
||||||
Height="26"
|
Height="26"
|
||||||
CornerRadius="6"
|
CornerRadius="12"
|
||||||
BorderThickness="0" />
|
BorderThickness="0" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button x:Name="RecommendedColorButton6"
|
<Button x:Name="RecommendedColorButton6"
|
||||||
@@ -817,7 +863,7 @@
|
|||||||
<Border x:Name="RecommendedColorSwatch6"
|
<Border x:Name="RecommendedColorSwatch6"
|
||||||
Width="26"
|
Width="26"
|
||||||
Height="26"
|
Height="26"
|
||||||
CornerRadius="6"
|
CornerRadius="12"
|
||||||
BorderThickness="0" />
|
BorderThickness="0" />
|
||||||
</Button>
|
</Button>
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
@@ -852,7 +898,7 @@
|
|||||||
<Border x:Name="MonetColorSwatch1"
|
<Border x:Name="MonetColorSwatch1"
|
||||||
Width="26"
|
Width="26"
|
||||||
Height="26"
|
Height="26"
|
||||||
CornerRadius="6"
|
CornerRadius="12"
|
||||||
BorderThickness="0" />
|
BorderThickness="0" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button x:Name="MonetColorButton2"
|
<Button x:Name="MonetColorButton2"
|
||||||
@@ -863,7 +909,7 @@
|
|||||||
<Border x:Name="MonetColorSwatch2"
|
<Border x:Name="MonetColorSwatch2"
|
||||||
Width="26"
|
Width="26"
|
||||||
Height="26"
|
Height="26"
|
||||||
CornerRadius="6"
|
CornerRadius="12"
|
||||||
BorderThickness="0" />
|
BorderThickness="0" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button x:Name="MonetColorButton3"
|
<Button x:Name="MonetColorButton3"
|
||||||
@@ -874,7 +920,7 @@
|
|||||||
<Border x:Name="MonetColorSwatch3"
|
<Border x:Name="MonetColorSwatch3"
|
||||||
Width="26"
|
Width="26"
|
||||||
Height="26"
|
Height="26"
|
||||||
CornerRadius="6"
|
CornerRadius="12"
|
||||||
BorderThickness="0" />
|
BorderThickness="0" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button x:Name="MonetColorButton4"
|
<Button x:Name="MonetColorButton4"
|
||||||
@@ -885,7 +931,7 @@
|
|||||||
<Border x:Name="MonetColorSwatch4"
|
<Border x:Name="MonetColorSwatch4"
|
||||||
Width="26"
|
Width="26"
|
||||||
Height="26"
|
Height="26"
|
||||||
CornerRadius="6"
|
CornerRadius="12"
|
||||||
BorderThickness="0" />
|
BorderThickness="0" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button x:Name="MonetColorButton5"
|
<Button x:Name="MonetColorButton5"
|
||||||
@@ -896,7 +942,7 @@
|
|||||||
<Border x:Name="MonetColorSwatch5"
|
<Border x:Name="MonetColorSwatch5"
|
||||||
Width="26"
|
Width="26"
|
||||||
Height="26"
|
Height="26"
|
||||||
CornerRadius="6"
|
CornerRadius="12"
|
||||||
BorderThickness="0" />
|
BorderThickness="0" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button x:Name="MonetColorButton6"
|
<Button x:Name="MonetColorButton6"
|
||||||
@@ -907,7 +953,7 @@
|
|||||||
<Border x:Name="MonetColorSwatch6"
|
<Border x:Name="MonetColorSwatch6"
|
||||||
Width="26"
|
Width="26"
|
||||||
Height="26"
|
Height="26"
|
||||||
CornerRadius="6"
|
CornerRadius="12"
|
||||||
BorderThickness="0" />
|
BorderThickness="0" />
|
||||||
</Button>
|
</Button>
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
@@ -932,7 +978,8 @@
|
|||||||
|
|
||||||
<ui:SettingsExpander x:Name="StatusBarClockSettingsExpander"
|
<ui:SettingsExpander x:Name="StatusBarClockSettingsExpander"
|
||||||
Header="时间组件"
|
Header="时间组件"
|
||||||
Description="在顶部状态栏显示时钟。">
|
Description="在顶部状态栏显示时钟。"
|
||||||
|
IsExpanded="False">
|
||||||
<ui:SettingsExpander.IconSource>
|
<ui:SettingsExpander.IconSource>
|
||||||
|
|
||||||
</ui:SettingsExpander.IconSource>
|
</ui:SettingsExpander.IconSource>
|
||||||
@@ -943,11 +990,77 @@
|
|||||||
Checked="OnStatusBarClockChecked"
|
Checked="OnStatusBarClockChecked"
|
||||||
Unchecked="OnStatusBarClockUnchecked" />
|
Unchecked="OnStatusBarClockUnchecked" />
|
||||||
</ui:SettingsExpander.Footer>
|
</ui:SettingsExpander.Footer>
|
||||||
</ui:SettingsExpander>
|
<StackPanel Margin="0,8,0,0" Spacing="12">
|
||||||
</StackPanel>
|
<TextBlock Text="显示格式" FontSize="14" FontWeight="SemiBold" Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||||
|
<RadioButton x:Name="ClockFormatHMSSRadio"
|
||||||
|
Content="时分秒 (HH:mm:ss)"
|
||||||
|
GroupName="ClockFormat"
|
||||||
|
Checked="OnClockFormatChanged"
|
||||||
|
Tag="Hms" />
|
||||||
|
<RadioButton x:Name="ClockFormatHMRadio"
|
||||||
|
Content="时分 (HH:mm)"
|
||||||
|
GroupName="ClockFormat"
|
||||||
|
Checked="OnClockFormatChanged"
|
||||||
|
Tag="Hm" />
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
<StackPanel x:Name="RegionSettingsPanel"
|
<ui:SettingsExpander x:Name="StatusBarSpacingSettingsExpander"
|
||||||
IsVisible="False"
|
Header="Component spacing"
|
||||||
|
Description="Adjust spacing between status bar components."
|
||||||
|
IsExpanded="False">
|
||||||
|
<ui:SettingsExpander.IconSource>
|
||||||
|
|
||||||
|
</ui:SettingsExpander.IconSource>
|
||||||
|
<ui:SettingsExpander.Footer>
|
||||||
|
<ComboBox x:Name="StatusBarSpacingModeComboBox"
|
||||||
|
Width="220"
|
||||||
|
SelectionChanged="OnStatusBarSpacingModeChanged">
|
||||||
|
<ComboBoxItem x:Name="StatusBarSpacingModeCompactItem" Tag="Compact" Content="Compact" />
|
||||||
|
<ComboBoxItem x:Name="StatusBarSpacingModeRelaxedItem" Tag="Relaxed" Content="Relaxed" />
|
||||||
|
<ComboBoxItem x:Name="StatusBarSpacingModeCustomItem" Tag="Custom" Content="Custom" />
|
||||||
|
</ComboBox>
|
||||||
|
</ui:SettingsExpander.Footer>
|
||||||
|
|
||||||
|
<StackPanel x:Name="StatusBarSpacingCustomPanel"
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
Spacing="12"
|
||||||
|
IsVisible="False">
|
||||||
|
<TextBlock x:Name="StatusBarSpacingCustomLabelTextBlock"
|
||||||
|
FontSize="14"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||||
|
Text="Custom spacing" />
|
||||||
|
|
||||||
|
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="12">
|
||||||
|
<Slider x:Name="StatusBarSpacingSlider"
|
||||||
|
Grid.Column="0"
|
||||||
|
Minimum="0"
|
||||||
|
Maximum="30"
|
||||||
|
TickFrequency="1"
|
||||||
|
TickPlacement="None"
|
||||||
|
Value="12"
|
||||||
|
ValueChanged="OnStatusBarSpacingSliderChanged" />
|
||||||
|
<ui:NumberBox x:Name="StatusBarSpacingNumberBox"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="80"
|
||||||
|
Minimum="0"
|
||||||
|
Maximum="30"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||||
|
Value="12" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<TextBlock x:Name="StatusBarSpacingComputedPxTextBlock"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"
|
||||||
|
Text="≈ 0 px" />
|
||||||
|
</StackPanel>
|
||||||
|
</ui:SettingsExpander>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel x:Name="RegionSettingsPanel"
|
||||||
|
IsVisible="False"
|
||||||
Spacing="16">
|
Spacing="16">
|
||||||
<TextBlock x:Name="RegionPanelTitleTextBlock"
|
<TextBlock x:Name="RegionPanelTitleTextBlock"
|
||||||
FontSize="24"
|
FontSize="24"
|
||||||
@@ -983,6 +1096,19 @@
|
|||||||
</ui:SettingsExpander.Footer>
|
</ui:SettingsExpander.Footer>
|
||||||
</ui:SettingsExpander>
|
</ui:SettingsExpander>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel x:Name="AboutSettingsPanel" IsVisible="False" Spacing="20">
|
||||||
|
<TextBlock x:Name="AboutPanelTitleTextBlock" FontSize="24" FontWeight="SemiBold" Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" Text="关于" />
|
||||||
|
<Border Background="{DynamicResource AdaptiveSurfaceRaisedBrush}" CornerRadius="20" Padding="20">
|
||||||
|
<StackPanel Spacing="12">
|
||||||
|
<TextBlock Text="LanMontainDesktop" FontSize="20" FontWeight="SemiBold" Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||||
|
<TextBlock Text="现代化桌面壳层应用" FontSize="13" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
|
||||||
|
<Separator Background="{DynamicResource AdaptiveButtonBorderBrush}" Margin="0,8" />
|
||||||
|
<TextBlock x:Name="VersionTextBlock" Text="版本号: 1.0.0" FontSize="13" Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||||
|
<TextBlock x:Name="CodeNameTextBlock" Text="版本代号: Administrate" FontSize="13" FontWeight="SemiBold" Foreground="{DynamicResource AdaptiveAccentBrush}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -990,6 +1116,47 @@
|
|||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<Border x:Name="ComponentSettingsWindow"
|
||||||
|
IsVisible="False"
|
||||||
|
Opacity="0"
|
||||||
|
Classes="glass-strong"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
Width="400"
|
||||||
|
MinWidth="300"
|
||||||
|
MaxWidth="500"
|
||||||
|
Height="300"
|
||||||
|
MinHeight="200"
|
||||||
|
Margin="24,24,24,100"
|
||||||
|
CornerRadius="36"
|
||||||
|
Padding="0">
|
||||||
|
<Grid RowDefinitions="Auto,*">
|
||||||
|
<Border Grid.Row="0"
|
||||||
|
Background="{DynamicResource AdaptiveAccentBrush}"
|
||||||
|
CornerRadius="36,36,0,0"
|
||||||
|
Padding="16,12">
|
||||||
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
|
<TextBlock Text="组件设置"
|
||||||
|
FontSize="16"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="White"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<Button Grid.Column="1"
|
||||||
|
Padding="8"
|
||||||
|
Background="Transparent"
|
||||||
|
BorderThickness="0"
|
||||||
|
Click="OnCloseComponentSettingsClick">
|
||||||
|
<fi:FluentIcon Icon="Dismiss"
|
||||||
|
FontSize="14"
|
||||||
|
Foreground="White" />
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
<ContentControl x:Name="ComponentSettingsContentHost"
|
||||||
|
Grid.Row="1" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
<Border x:Name="ComponentLibraryWindow"
|
<Border x:Name="ComponentLibraryWindow"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
Opacity="0"
|
Opacity="0"
|
||||||
@@ -1002,8 +1169,11 @@
|
|||||||
Height="260"
|
Height="260"
|
||||||
MinHeight="220"
|
MinHeight="220"
|
||||||
Margin="24,24,24,100"
|
Margin="24,24,24,100"
|
||||||
CornerRadius="18"
|
CornerRadius="36"
|
||||||
Padding="14">
|
Padding="14"
|
||||||
|
PointerPressed="OnComponentLibraryWindowPointerPressed"
|
||||||
|
PointerMoved="OnComponentLibraryWindowPointerMoved"
|
||||||
|
PointerReleased="OnComponentLibraryWindowPointerReleased">
|
||||||
<Border.Transitions>
|
<Border.Transitions>
|
||||||
<Transitions>
|
<Transitions>
|
||||||
<DoubleTransition Property="Opacity" Duration="0:0:0.2" />
|
<DoubleTransition Property="Opacity" Duration="0:0:0.2" />
|
||||||
@@ -1018,7 +1188,7 @@
|
|||||||
FontSize="16"
|
FontSize="16"
|
||||||
FontWeight="SemiBold"
|
FontWeight="SemiBold"
|
||||||
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||||
Text="组件库" />
|
Text="小组件" />
|
||||||
<Button x:Name="CloseComponentLibraryButton"
|
<Button x:Name="CloseComponentLibraryButton"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Padding="8"
|
Padding="8"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using FluentAvalonia.UI.Controls;
|
using FluentAvalonia.UI.Controls;
|
||||||
using Avalonia.Layout;
|
using Avalonia.Layout;
|
||||||
@@ -22,6 +23,7 @@ using LanMontainDesktop.ComponentSystem.Extensions;
|
|||||||
using LanMontainDesktop.Models;
|
using LanMontainDesktop.Models;
|
||||||
using LanMontainDesktop.Services;
|
using LanMontainDesktop.Services;
|
||||||
using LanMontainDesktop.Theme;
|
using LanMontainDesktop.Theme;
|
||||||
|
using LanMontainDesktop.Views.Components;
|
||||||
using LibVLCSharp.Shared;
|
using LibVLCSharp.Shared;
|
||||||
|
|
||||||
namespace LanMontainDesktop.Views;
|
namespace LanMontainDesktop.Views;
|
||||||
@@ -47,6 +49,9 @@ public partial class MainWindow : Window
|
|||||||
private const int StatusBarRowIndex = 0;
|
private const int StatusBarRowIndex = 0;
|
||||||
private const int MinShortSideCells = 6;
|
private const int MinShortSideCells = 6;
|
||||||
private const int MaxShortSideCells = 96;
|
private const int MaxShortSideCells = 96;
|
||||||
|
private const int MinEdgeInsetPercent = 0;
|
||||||
|
private const int MaxEdgeInsetPercent = 30;
|
||||||
|
private const int DefaultEdgeInsetPercent = 18;
|
||||||
private const int SettingsTransitionDurationMs = 240;
|
private const int SettingsTransitionDurationMs = 240;
|
||||||
private const double WallpaperPreviewMaxWidth = 520;
|
private const double WallpaperPreviewMaxWidth = 520;
|
||||||
private const double LightBackgroundLuminanceThreshold = 0.57;
|
private const double LightBackgroundLuminanceThreshold = 0.57;
|
||||||
@@ -64,7 +69,17 @@ public partial class MainWindow : Window
|
|||||||
TaskbarActionId.MinimizeToWindows,
|
TaskbarActionId.MinimizeToWindows,
|
||||||
TaskbarActionId.OpenSettings
|
TaskbarActionId.OpenSettings
|
||||||
];
|
];
|
||||||
private readonly record struct GridMetrics(int ColumnCount, int RowCount, double CellSize);
|
private readonly record struct GridMetrics(
|
||||||
|
int ColumnCount,
|
||||||
|
int RowCount,
|
||||||
|
double CellSize,
|
||||||
|
double GapPx,
|
||||||
|
double EdgeInsetPx,
|
||||||
|
double GridWidthPx,
|
||||||
|
double GridHeightPx)
|
||||||
|
{
|
||||||
|
public double Pitch => CellSize + GapPx;
|
||||||
|
}
|
||||||
private readonly MonetColorService _monetColorService = new();
|
private readonly MonetColorService _monetColorService = new();
|
||||||
private readonly AppSettingsService _appSettingsService = new();
|
private readonly AppSettingsService _appSettingsService = new();
|
||||||
private readonly LocalizationService _localizationService = new();
|
private readonly LocalizationService _localizationService = new();
|
||||||
@@ -87,6 +102,7 @@ public partial class MainWindow : Window
|
|||||||
private bool _suppressSettingsPersistence;
|
private bool _suppressSettingsPersistence;
|
||||||
private bool _isUpdatingWallpaperPreviewLayout;
|
private bool _isUpdatingWallpaperPreviewLayout;
|
||||||
private bool _isComponentLibraryOpen;
|
private bool _isComponentLibraryOpen;
|
||||||
|
private Border? _selectedDesktopComponentHost;
|
||||||
private bool _reopenSettingsAfterComponentLibraryClose;
|
private bool _reopenSettingsAfterComponentLibraryClose;
|
||||||
private TranslateTransform? _settingsContentPanelTransform;
|
private TranslateTransform? _settingsContentPanelTransform;
|
||||||
private IBrush? _defaultDesktopBackground;
|
private IBrush? _defaultDesktopBackground;
|
||||||
@@ -104,8 +120,20 @@ public partial class MainWindow : Window
|
|||||||
private IReadOnlyList<Color> _monetColors = Array.Empty<Color>();
|
private IReadOnlyList<Color> _monetColors = Array.Empty<Color>();
|
||||||
private Color _selectedThemeColor = Color.Parse("#FF3B82F6");
|
private Color _selectedThemeColor = Color.Parse("#FF3B82F6");
|
||||||
private double _currentDesktopCellSize;
|
private double _currentDesktopCellSize;
|
||||||
|
private double _currentDesktopCellGap;
|
||||||
|
private double _currentDesktopEdgeInset;
|
||||||
|
private string _gridSpacingPreset = "Relaxed";
|
||||||
|
private string _statusBarSpacingMode = "Relaxed";
|
||||||
|
private int _statusBarCustomSpacingPercent = 12;
|
||||||
|
private bool _suppressGridSpacingEvents;
|
||||||
|
private bool _suppressGridInsetEvents;
|
||||||
|
private bool _suppressStatusBarSpacingEvents;
|
||||||
|
private int _desktopEdgeInsetPercent = DefaultEdgeInsetPercent;
|
||||||
private string _taskbarLayoutMode = TaskbarLayoutBottomFullRowMacStyle;
|
private string _taskbarLayoutMode = TaskbarLayoutBottomFullRowMacStyle;
|
||||||
private string _languageCode = "zh-CN";
|
private string _languageCode = "zh-CN";
|
||||||
|
private ClockDisplayFormat _clockDisplayFormat = ClockDisplayFormat.HourMinuteSecond;
|
||||||
|
|
||||||
|
private double CurrentDesktopPitch => _currentDesktopCellSize + _currentDesktopCellGap;
|
||||||
|
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
@@ -131,12 +159,40 @@ public partial class MainWindow : Window
|
|||||||
snapshot.GridShortSideCells > 0 ? snapshot.GridShortSideCells : CalculateDefaultShortSideCellCountFromDpi(),
|
snapshot.GridShortSideCells > 0 ? snapshot.GridShortSideCells : CalculateDefaultShortSideCellCountFromDpi(),
|
||||||
MinShortSideCells,
|
MinShortSideCells,
|
||||||
MaxShortSideCells);
|
MaxShortSideCells);
|
||||||
|
|
||||||
|
_gridSpacingPreset = NormalizeGridSpacingPreset(snapshot.GridSpacingPreset);
|
||||||
|
_suppressGridSpacingEvents = true;
|
||||||
|
GridSpacingPresetComboBox.SelectedIndex = string.Equals(_gridSpacingPreset, "Compact", StringComparison.OrdinalIgnoreCase) ? 1 : 0;
|
||||||
|
_suppressGridSpacingEvents = false;
|
||||||
|
|
||||||
|
_desktopEdgeInsetPercent = Math.Clamp(snapshot.DesktopEdgeInsetPercent, MinEdgeInsetPercent, MaxEdgeInsetPercent);
|
||||||
|
_suppressGridInsetEvents = true;
|
||||||
|
GridEdgeInsetSlider.Value = _desktopEdgeInsetPercent;
|
||||||
|
GridEdgeInsetNumberBox.Value = _desktopEdgeInsetPercent;
|
||||||
|
_suppressGridInsetEvents = false;
|
||||||
|
GridEdgeInsetNumberBox.ValueChanged += OnGridEdgeInsetNumberBoxChanged;
|
||||||
|
|
||||||
|
_statusBarSpacingMode = NormalizeStatusBarSpacingMode(snapshot.StatusBarSpacingMode);
|
||||||
|
_statusBarCustomSpacingPercent = Math.Clamp(snapshot.StatusBarCustomSpacingPercent, 0, 30);
|
||||||
|
_suppressStatusBarSpacingEvents = true;
|
||||||
|
StatusBarSpacingModeComboBox.SelectedIndex = _statusBarSpacingMode switch
|
||||||
|
{
|
||||||
|
"Compact" => 0,
|
||||||
|
"Custom" => 2,
|
||||||
|
_ => 1
|
||||||
|
};
|
||||||
|
StatusBarSpacingSlider.Value = _statusBarCustomSpacingPercent;
|
||||||
|
StatusBarSpacingNumberBox.Value = _statusBarCustomSpacingPercent;
|
||||||
|
StatusBarSpacingCustomPanel.IsVisible = string.Equals(_statusBarSpacingMode, "Custom", StringComparison.OrdinalIgnoreCase);
|
||||||
|
_suppressStatusBarSpacingEvents = false;
|
||||||
|
StatusBarSpacingNumberBox.ValueChanged += OnStatusBarSpacingNumberBoxChanged;
|
||||||
|
|
||||||
GridSizeNumberBox.Value = _targetShortSideCells;
|
GridSizeNumberBox.Value = _targetShortSideCells;
|
||||||
GridSizeSlider.Value = _targetShortSideCells;
|
GridSizeSlider.Value = _targetShortSideCells;
|
||||||
GridSizeSlider.ValueChanged += OnGridSizeSliderChanged;
|
GridSizeSlider.ValueChanged += OnGridSizeSliderChanged;
|
||||||
GridSizeNumberBox.ValueChanged += OnGridSizeNumberBoxChanged;
|
GridSizeNumberBox.ValueChanged += OnGridSizeNumberBoxChanged;
|
||||||
|
|
||||||
SettingsNavListBox.SelectedIndex = Math.Clamp(snapshot.SettingsTabIndex, 0, 4);
|
SettingsNavListBox.SelectedIndex = Math.Clamp(snapshot.SettingsTabIndex, 0, 5);
|
||||||
UpdateSettingsTabContent();
|
UpdateSettingsTabContent();
|
||||||
|
|
||||||
WallpaperPlacementComboBox.SelectedIndex = GetPlacementIndexFromSetting(snapshot.WallpaperPlacement);
|
WallpaperPlacementComboBox.SelectedIndex = GetPlacementIndexFromSetting(snapshot.WallpaperPlacement);
|
||||||
@@ -199,6 +255,8 @@ public partial class MainWindow : Window
|
|||||||
GridPreviewHost.SizeChanged -= OnGridPreviewHostSizeChanged;
|
GridPreviewHost.SizeChanged -= OnGridPreviewHostSizeChanged;
|
||||||
GridSizeSlider.ValueChanged -= OnGridSizeSliderChanged;
|
GridSizeSlider.ValueChanged -= OnGridSizeSliderChanged;
|
||||||
GridSizeNumberBox.ValueChanged -= OnGridSizeNumberBoxChanged;
|
GridSizeNumberBox.ValueChanged -= OnGridSizeNumberBoxChanged;
|
||||||
|
GridEdgeInsetNumberBox.ValueChanged -= OnGridEdgeInsetNumberBoxChanged;
|
||||||
|
StatusBarSpacingNumberBox.ValueChanged -= OnStatusBarSpacingNumberBoxChanged;
|
||||||
base.OnClosed(e);
|
base.OnClosed(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,12 +303,152 @@ public partial class MainWindow : Window
|
|||||||
UpdateGridPreviewLayout();
|
UpdateGridPreviewLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnGridEdgeInsetSliderChanged(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (_suppressGridInsetEvents)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = (int)Math.Round(GridEdgeInsetSlider.Value);
|
||||||
|
SetPendingGridEdgeInsetPercent(value, updateSlider: false, updateNumberBox: true);
|
||||||
|
UpdateGridPreviewLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGridEdgeInsetNumberBoxChanged(object? sender, NumberBoxValueChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (_suppressGridInsetEvents)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = (int)Math.Round(GridEdgeInsetNumberBox.Value);
|
||||||
|
SetPendingGridEdgeInsetPercent(value, updateSlider: true, updateNumberBox: false);
|
||||||
|
UpdateGridPreviewLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetPendingGridEdgeInsetPercent(int percent, bool updateSlider, bool updateNumberBox)
|
||||||
|
{
|
||||||
|
var clamped = Math.Clamp(percent, MinEdgeInsetPercent, MaxEdgeInsetPercent);
|
||||||
|
|
||||||
|
_suppressGridInsetEvents = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (updateSlider && Math.Abs(GridEdgeInsetSlider.Value - clamped) > double.Epsilon)
|
||||||
|
{
|
||||||
|
GridEdgeInsetSlider.Value = clamped;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateNumberBox && Math.Abs(GridEdgeInsetNumberBox.Value - clamped) > double.Epsilon)
|
||||||
|
{
|
||||||
|
GridEdgeInsetNumberBox.Value = clamped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_suppressGridInsetEvents = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGridSpacingPresetSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (_suppressGridSpacingEvents)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateGridPreviewLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStatusBarSpacingModeChanged(object? sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (_suppressStatusBarSpacingEvents)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_statusBarSpacingMode = NormalizeStatusBarSpacingMode(
|
||||||
|
TryGetSelectedComboBoxTag(StatusBarSpacingModeComboBox) ?? _statusBarSpacingMode);
|
||||||
|
|
||||||
|
StatusBarSpacingCustomPanel.IsVisible = string.Equals(_statusBarSpacingMode, "Custom", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
ApplyDesktopStatusBarComponentSpacing();
|
||||||
|
UpdateWallpaperPreviewLayout();
|
||||||
|
UpdateGridPreviewLayout();
|
||||||
|
SchedulePersistSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStatusBarSpacingSliderChanged(object? sender, RangeBaseValueChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (_suppressStatusBarSpacingEvents)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var percent = (int)Math.Round(StatusBarSpacingSlider.Value);
|
||||||
|
SetStatusBarCustomSpacingPercent(percent, updateSlider: false, updateNumberBox: true);
|
||||||
|
|
||||||
|
if (string.Equals(_statusBarSpacingMode, "Custom", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
ApplyDesktopStatusBarComponentSpacing();
|
||||||
|
UpdateWallpaperPreviewLayout();
|
||||||
|
UpdateGridPreviewLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
SchedulePersistSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStatusBarSpacingNumberBoxChanged(object? sender, NumberBoxValueChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (_suppressStatusBarSpacingEvents)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var percent = (int)Math.Round(StatusBarSpacingNumberBox.Value);
|
||||||
|
SetStatusBarCustomSpacingPercent(percent, updateSlider: true, updateNumberBox: false);
|
||||||
|
|
||||||
|
if (string.Equals(_statusBarSpacingMode, "Custom", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
ApplyDesktopStatusBarComponentSpacing();
|
||||||
|
UpdateWallpaperPreviewLayout();
|
||||||
|
UpdateGridPreviewLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
SchedulePersistSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetStatusBarCustomSpacingPercent(int percent, bool updateSlider, bool updateNumberBox)
|
||||||
|
{
|
||||||
|
percent = Math.Clamp(percent, 0, 30);
|
||||||
|
_statusBarCustomSpacingPercent = percent;
|
||||||
|
|
||||||
|
_suppressStatusBarSpacingEvents = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (updateSlider && Math.Abs(StatusBarSpacingSlider.Value - percent) > double.Epsilon)
|
||||||
|
{
|
||||||
|
StatusBarSpacingSlider.Value = percent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateNumberBox && Math.Abs(StatusBarSpacingNumberBox.Value - percent) > double.Epsilon)
|
||||||
|
{
|
||||||
|
StatusBarSpacingNumberBox.Value = percent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_suppressStatusBarSpacingEvents = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateGridPreviewLayout()
|
private void UpdateGridPreviewLayout()
|
||||||
{
|
{
|
||||||
if (GridPreviewFrame is null ||
|
if (GridPreviewFrame is null ||
|
||||||
GridPreviewHost is null ||
|
GridPreviewHost is null ||
|
||||||
GridPreviewViewport is null ||
|
GridPreviewViewport is null ||
|
||||||
GridPreviewGrid is null)
|
GridPreviewGrid is null ||
|
||||||
|
GridPreviewLinesCanvas is null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -279,14 +477,24 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
var innerWidth = Math.Max(1, gridPreviewWidth - horizontalPadding);
|
var innerWidth = Math.Max(1, gridPreviewWidth - horizontalPadding);
|
||||||
var innerHeight = Math.Max(1, gridPreviewHeight - verticalPadding);
|
var innerHeight = Math.Max(1, gridPreviewHeight - verticalPadding);
|
||||||
var gridMetrics = CalculateGridMetrics(innerWidth, innerHeight, previewShortSideCells);
|
var preset = NormalizeGridSpacingPreset(TryGetSelectedComboBoxTag(GridSpacingPresetComboBox) ?? _gridSpacingPreset);
|
||||||
|
var gapRatio = ResolveGridGapRatio(preset);
|
||||||
|
var pendingEdgeInsetPercent = ResolvePendingGridEdgeInsetPercent();
|
||||||
|
var edgeInset = CalculateEdgeInset(innerWidth, innerHeight, previewShortSideCells, pendingEdgeInsetPercent);
|
||||||
|
var gridMetrics = CalculateGridMetrics(innerWidth, innerHeight, previewShortSideCells, gapRatio, edgeInset);
|
||||||
if (gridMetrics.CellSize <= 0)
|
if (gridMetrics.CellSize <= 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GridPreviewGrid.Width = gridMetrics.ColumnCount * gridMetrics.CellSize;
|
var inset = new Thickness(gridMetrics.EdgeInsetPx);
|
||||||
GridPreviewGrid.Height = gridMetrics.RowCount * gridMetrics.CellSize;
|
GridPreviewGrid.Margin = inset;
|
||||||
|
GridPreviewGrid.RowSpacing = gridMetrics.GapPx;
|
||||||
|
GridPreviewGrid.ColumnSpacing = gridMetrics.GapPx;
|
||||||
|
GridPreviewGrid.Width = gridMetrics.GridWidthPx;
|
||||||
|
GridPreviewGrid.Height = gridMetrics.GridHeightPx;
|
||||||
|
|
||||||
|
GridPreviewLinesCanvas.Margin = inset;
|
||||||
|
|
||||||
GridPreviewGrid.RowDefinitions.Clear();
|
GridPreviewGrid.RowDefinitions.Clear();
|
||||||
GridPreviewGrid.ColumnDefinitions.Clear();
|
GridPreviewGrid.ColumnDefinitions.Clear();
|
||||||
@@ -316,6 +524,8 @@ public partial class MainWindow : Window
|
|||||||
Grid.SetColumnSpan(GridPreviewBottomTaskbarContainer, gridMetrics.ColumnCount);
|
Grid.SetColumnSpan(GridPreviewBottomTaskbarContainer, gridMetrics.ColumnCount);
|
||||||
|
|
||||||
ApplyGridPreviewWidgetSizing(gridMetrics.CellSize);
|
ApplyGridPreviewWidgetSizing(gridMetrics.CellSize);
|
||||||
|
ApplyStatusBarComponentSpacingForPanel(GridPreviewTopStatusComponentsPanel, gridMetrics.CellSize);
|
||||||
|
UpdateGridEdgeInsetComputedPxText(gridMetrics.CellSize);
|
||||||
|
|
||||||
GridInfoTextBlock.Text = Lf(
|
GridInfoTextBlock.Text = Lf(
|
||||||
"settings.grid.info_format",
|
"settings.grid.info_format",
|
||||||
@@ -344,21 +554,19 @@ public partial class MainWindow : Window
|
|||||||
GridPreviewLinesCanvas.Children.Clear();
|
GridPreviewLinesCanvas.Children.Clear();
|
||||||
|
|
||||||
var cellSize = gridMetrics.CellSize;
|
var cellSize = gridMetrics.CellSize;
|
||||||
var gridWidth = gridMetrics.ColumnCount * cellSize;
|
var pitch = gridMetrics.Pitch;
|
||||||
var gridHeight = gridMetrics.RowCount * cellSize;
|
var gridWidth = gridMetrics.GridWidthPx;
|
||||||
|
var gridHeight = gridMetrics.GridHeightPx;
|
||||||
|
|
||||||
GridPreviewLinesCanvas.Width = gridWidth;
|
GridPreviewLinesCanvas.Width = gridWidth;
|
||||||
GridPreviewLinesCanvas.Height = gridHeight;
|
GridPreviewLinesCanvas.Height = gridHeight;
|
||||||
|
|
||||||
Canvas.SetLeft(GridPreviewLinesCanvas, 0);
|
|
||||||
Canvas.SetTop(GridPreviewLinesCanvas, 0);
|
|
||||||
|
|
||||||
var dashLength = cellSize * 0.3;
|
var dashLength = cellSize * 0.3;
|
||||||
var gapLength = cellSize * 0.2;
|
var gapLength = cellSize * 0.2;
|
||||||
|
|
||||||
for (var row = 0; row <= gridMetrics.RowCount; row++)
|
for (var row = 0; row <= gridMetrics.RowCount; row++)
|
||||||
{
|
{
|
||||||
var y = row * cellSize;
|
var y = row == gridMetrics.RowCount ? gridHeight : row * pitch;
|
||||||
var line = new Line
|
var line = new Line
|
||||||
{
|
{
|
||||||
StartPoint = new Point(0, y),
|
StartPoint = new Point(0, y),
|
||||||
@@ -373,7 +581,7 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
for (var col = 0; col <= gridMetrics.ColumnCount; col++)
|
for (var col = 0; col <= gridMetrics.ColumnCount; col++)
|
||||||
{
|
{
|
||||||
var x = col * cellSize;
|
var x = col == gridMetrics.ColumnCount ? gridWidth : col * pitch;
|
||||||
var line = new Line
|
var line = new Line
|
||||||
{
|
{
|
||||||
StartPoint = new Point(x, 0),
|
StartPoint = new Point(x, 0),
|
||||||
@@ -389,13 +597,12 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
private void ApplyGridPreviewWidgetSizing(double cellSize)
|
private void ApplyGridPreviewWidgetSizing(double cellSize)
|
||||||
{
|
{
|
||||||
var margin = Math.Clamp(cellSize * 0.08, 1, 6);
|
var previewTaskbarCell = Math.Clamp(cellSize * 0.74, 10, 30);
|
||||||
var previewTaskbarCell = Math.Clamp(cellSize, 10, 36);
|
|
||||||
var iconSize = Math.Clamp(cellSize * 0.35, 8, 16);
|
var iconSize = Math.Clamp(cellSize * 0.35, 8, 16);
|
||||||
|
|
||||||
GridPreviewTopStatusBarHost.Padding = new Thickness(Math.Clamp(cellSize * 0.08, 1, 4));
|
GridPreviewTopStatusBarHost.Padding = new Thickness(0);
|
||||||
GridPreviewBottomTaskbarContainer.Margin = new Thickness(margin);
|
GridPreviewBottomTaskbarContainer.Margin = new Thickness(0);
|
||||||
GridPreviewBottomTaskbarContainer.CornerRadius = new CornerRadius(Math.Clamp(cellSize * 0.22, 4, 10));
|
GridPreviewBottomTaskbarContainer.CornerRadius = new CornerRadius(Math.Clamp(cellSize * 0.45, 16, 32));
|
||||||
GridPreviewBottomTaskbarContainer.Padding = new Thickness(Math.Clamp(cellSize * 0.06, 1, 4));
|
GridPreviewBottomTaskbarContainer.Padding = new Thickness(Math.Clamp(cellSize * 0.06, 1, 4));
|
||||||
|
|
||||||
GridPreviewBackButtonTextBlock.FontSize = Math.Clamp(cellSize * 0.19, 5, 13);
|
GridPreviewBackButtonTextBlock.FontSize = Math.Clamp(cellSize * 0.19, 5, 13);
|
||||||
@@ -411,6 +618,10 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
private void OnApplyGridSizeClick(object? sender, RoutedEventArgs e)
|
private void OnApplyGridSizeClick(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
_gridSpacingPreset = NormalizeGridSpacingPreset(
|
||||||
|
TryGetSelectedComboBoxTag(GridSpacingPresetComboBox) ?? _gridSpacingPreset);
|
||||||
|
_desktopEdgeInsetPercent = ResolvePendingGridEdgeInsetPercent();
|
||||||
|
|
||||||
var requested = (int)Math.Round(GridSizeNumberBox.Value);
|
var requested = (int)Math.Round(GridSizeNumberBox.Value);
|
||||||
if (requested <= 0)
|
if (requested <= 0)
|
||||||
{
|
{
|
||||||
@@ -429,26 +640,56 @@ public partial class MainWindow : Window
|
|||||||
GridSizeSlider.Value = _targetShortSideCells;
|
GridSizeSlider.Value = _targetShortSideCells;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetPendingGridEdgeInsetPercent(_desktopEdgeInsetPercent, updateSlider: true, updateNumberBox: true);
|
||||||
|
|
||||||
RebuildDesktopGrid();
|
RebuildDesktopGrid();
|
||||||
PersistSettings();
|
PersistSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnClockFormatChanged(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is not RadioButton radioButton || radioButton.Tag is not string formatTag)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_clockDisplayFormat = formatTag == "Hm"
|
||||||
|
? ClockDisplayFormat.HourMinute
|
||||||
|
: ClockDisplayFormat.HourMinuteSecond;
|
||||||
|
|
||||||
|
if (ClockWidget is ClockWidget clock)
|
||||||
|
{
|
||||||
|
clock.SetDisplayFormat(_clockDisplayFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyTopStatusComponentVisibility();
|
||||||
|
UpdateWallpaperPreviewLayout();
|
||||||
|
PersistSettings();
|
||||||
|
}
|
||||||
|
|
||||||
private void RebuildDesktopGrid()
|
private void RebuildDesktopGrid()
|
||||||
{
|
{
|
||||||
var gridMetrics = CalculateGridMetrics(
|
var hostWidth = DesktopHost.Bounds.Width;
|
||||||
DesktopHost.Bounds.Width,
|
var hostHeight = DesktopHost.Bounds.Height;
|
||||||
DesktopHost.Bounds.Height,
|
var gapRatio = ResolveGridGapRatio(_gridSpacingPreset);
|
||||||
_targetShortSideCells);
|
var edgeInset = CalculateEdgeInset(hostWidth, hostHeight, _targetShortSideCells, _desktopEdgeInsetPercent);
|
||||||
|
var gridMetrics = CalculateGridMetrics(hostWidth, hostHeight, _targetShortSideCells, gapRatio, edgeInset);
|
||||||
if (gridMetrics.CellSize <= 0)
|
if (gridMetrics.CellSize <= 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_currentDesktopCellSize = gridMetrics.CellSize;
|
_currentDesktopCellSize = gridMetrics.CellSize;
|
||||||
|
_currentDesktopCellGap = gridMetrics.GapPx;
|
||||||
|
_currentDesktopEdgeInset = gridMetrics.EdgeInsetPx;
|
||||||
|
UpdateGridEdgeInsetComputedPxText(gridMetrics.CellSize);
|
||||||
|
|
||||||
DesktopGrid.RowDefinitions.Clear();
|
DesktopGrid.RowDefinitions.Clear();
|
||||||
DesktopGrid.ColumnDefinitions.Clear();
|
DesktopGrid.ColumnDefinitions.Clear();
|
||||||
DesktopGrid.Width = gridMetrics.ColumnCount * gridMetrics.CellSize;
|
DesktopGrid.Margin = new Thickness(gridMetrics.EdgeInsetPx);
|
||||||
DesktopGrid.Height = gridMetrics.RowCount * gridMetrics.CellSize;
|
DesktopGrid.RowSpacing = gridMetrics.GapPx;
|
||||||
|
DesktopGrid.ColumnSpacing = gridMetrics.GapPx;
|
||||||
|
DesktopGrid.Width = gridMetrics.GridWidthPx;
|
||||||
|
DesktopGrid.Height = gridMetrics.GridHeightPx;
|
||||||
|
|
||||||
for (var row = 0; row < gridMetrics.RowCount; row++)
|
for (var row = 0; row < gridMetrics.RowCount; row++)
|
||||||
{
|
{
|
||||||
@@ -476,6 +717,7 @@ public partial class MainWindow : Window
|
|||||||
ApplyTaskbarActionVisibility(GetCurrentTaskbarContext());
|
ApplyTaskbarActionVisibility(GetCurrentTaskbarContext());
|
||||||
|
|
||||||
ApplyWidgetSizing(gridMetrics.CellSize);
|
ApplyWidgetSizing(gridMetrics.CellSize);
|
||||||
|
ApplyDesktopStatusBarComponentSpacing();
|
||||||
UpdateDesktopSurfaceLayout(gridMetrics);
|
UpdateDesktopSurfaceLayout(gridMetrics);
|
||||||
UpdateSettingsViewportInsets(gridMetrics.CellSize);
|
UpdateSettingsViewportInsets(gridMetrics.CellSize);
|
||||||
|
|
||||||
@@ -489,26 +731,190 @@ public partial class MainWindow : Window
|
|||||||
UpdateWallpaperPreviewLayout();
|
UpdateWallpaperPreviewLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GridMetrics CalculateGridMetrics(double hostWidth, double hostHeight, int targetShortSideCells)
|
private void ApplyDesktopStatusBarComponentSpacing()
|
||||||
|
{
|
||||||
|
ApplyStatusBarComponentSpacingForPanel(TopStatusComponentsPanel, _currentDesktopCellSize);
|
||||||
|
UpdateStatusBarSpacingComputedPxText(_currentDesktopCellSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int ResolveStatusBarSpacingPercent()
|
||||||
|
{
|
||||||
|
return _statusBarSpacingMode switch
|
||||||
|
{
|
||||||
|
"Compact" => 6,
|
||||||
|
"Custom" => Math.Clamp(_statusBarCustomSpacingPercent, 0, 30),
|
||||||
|
_ => 12
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyStatusBarComponentSpacingForPanel(StackPanel? panel, double cellSize)
|
||||||
|
{
|
||||||
|
if (panel is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var percent = ResolveStatusBarSpacingPercent();
|
||||||
|
var spacingPx = Math.Max(0, cellSize) * (percent / 100d);
|
||||||
|
panel.Spacing = spacingPx;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStatusBarSpacingComputedPxText(double cellSize)
|
||||||
|
{
|
||||||
|
if (StatusBarSpacingComputedPxTextBlock is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var percent = ResolveStatusBarSpacingPercent();
|
||||||
|
var spacingPx = Math.Max(0, cellSize) * (percent / 100d);
|
||||||
|
StatusBarSpacingComputedPxTextBlock.Text = Lf(
|
||||||
|
"settings.status_bar.spacing_custom_px_format",
|
||||||
|
"鈮?{0:F1}px",
|
||||||
|
spacingPx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int ResolvePendingGridEdgeInsetPercent()
|
||||||
|
{
|
||||||
|
var pending = (int)Math.Round(GridEdgeInsetNumberBox.Value);
|
||||||
|
return Math.Clamp(pending, MinEdgeInsetPercent, MaxEdgeInsetPercent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateGridEdgeInsetComputedPxText(double cellSize)
|
||||||
|
{
|
||||||
|
if (GridEdgeInsetComputedPxTextBlock is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var percent = ResolvePendingGridEdgeInsetPercent();
|
||||||
|
var insetPx = Math.Clamp(Math.Max(0, cellSize) * (percent / 100d), 0, 80);
|
||||||
|
GridEdgeInsetComputedPxTextBlock.Text = Lf(
|
||||||
|
"settings.grid.edge_inset_px_format",
|
||||||
|
"{0:F1}px",
|
||||||
|
insetPx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeGridSpacingPreset(string? value)
|
||||||
|
{
|
||||||
|
return string.Equals(value, "Compact", StringComparison.OrdinalIgnoreCase)
|
||||||
|
? "Compact"
|
||||||
|
: "Relaxed";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeStatusBarSpacingMode(string? value)
|
||||||
|
{
|
||||||
|
return value switch
|
||||||
|
{
|
||||||
|
_ when string.Equals(value, "Compact", StringComparison.OrdinalIgnoreCase) => "Compact",
|
||||||
|
_ when string.Equals(value, "Custom", StringComparison.OrdinalIgnoreCase) => "Custom",
|
||||||
|
_ => "Relaxed"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? TryGetSelectedComboBoxTag(ComboBox? comboBox)
|
||||||
|
{
|
||||||
|
if (comboBox?.SelectedItem is ComboBoxItem item)
|
||||||
|
{
|
||||||
|
return item.Tag?.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return comboBox?.SelectedItem?.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double ResolveGridGapRatio(string preset)
|
||||||
|
{
|
||||||
|
return string.Equals(preset, "Compact", StringComparison.OrdinalIgnoreCase) ? 0.06 : 0.12;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double CalculateEdgeInset(double hostWidth, double hostHeight, int shortSideCells, int insetPercent)
|
||||||
|
{
|
||||||
|
if (hostWidth <= 1 || hostHeight <= 1)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cells = Math.Max(1, shortSideCells);
|
||||||
|
var shortSidePx = Math.Max(1, Math.Min(hostWidth, hostHeight));
|
||||||
|
var baseCell = shortSidePx / cells;
|
||||||
|
|
||||||
|
// --- 姣斾緥鍖栫暀鐧?(Proportional Inset) ---
|
||||||
|
// 鍏佽鐢ㄦ埛鐧惧垎姣旇皟鑺傦紝浣嗚瀹氭洿鍚堢悊鐨勫熀鍑嗗拰闄愬埗
|
||||||
|
var clampedPercent = Math.Clamp(insetPercent, MinEdgeInsetPercent, MaxEdgeInsetPercent);
|
||||||
|
var insetRatio = clampedPercent / 100d;
|
||||||
|
|
||||||
|
// 纭繚鏈€灏忕暀鐧借兘瀹圭撼涓€瀹氱殑闃村奖鎵╁睍
|
||||||
|
// 鍏佽 0 杈硅窛锛屾渶澶т笂闄愮淮鎸?80px
|
||||||
|
return Math.Clamp(baseCell * insetRatio, 0, 80);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GridMetrics CalculateGridMetrics(
|
||||||
|
double hostWidth,
|
||||||
|
double hostHeight,
|
||||||
|
int shortSideCells,
|
||||||
|
double gapRatio,
|
||||||
|
double edgeInsetPx)
|
||||||
{
|
{
|
||||||
if (hostWidth <= 1 || hostHeight <= 1)
|
if (hostWidth <= 1 || hostHeight <= 1)
|
||||||
{
|
{
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
var shortSideCells = Math.Max(1, targetShortSideCells);
|
var shortSide = Math.Max(1, shortSideCells);
|
||||||
|
var clampedGapRatio = Math.Max(0, gapRatio);
|
||||||
|
var inset = Math.Max(0, edgeInsetPx);
|
||||||
|
|
||||||
|
// Edge inset should come only from user setting.
|
||||||
|
// Remaining free space is handled by container centering, not baked into inset.
|
||||||
|
var availableWidth = Math.Max(1, hostWidth - inset * 2);
|
||||||
|
var availableHeight = Math.Max(1, hostHeight - inset * 2);
|
||||||
|
|
||||||
if (hostWidth >= hostHeight)
|
if (hostWidth >= hostHeight)
|
||||||
{
|
{
|
||||||
var rowCount = shortSideCells;
|
var rowCount = shortSide;
|
||||||
var cellSize = hostHeight / rowCount;
|
var denominator = rowCount + Math.Max(0, rowCount - 1) * clampedGapRatio;
|
||||||
var columnCount = Math.Max(1, (int)Math.Floor(hostWidth / cellSize));
|
if (denominator <= 0)
|
||||||
return new GridMetrics(columnCount, rowCount, cellSize);
|
{
|
||||||
}
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
var columns = shortSideCells;
|
var cellSize = availableHeight / denominator;
|
||||||
var size = hostWidth / columns;
|
var gapPx = cellSize * clampedGapRatio;
|
||||||
var rows = Math.Max(1, (int)Math.Floor(hostHeight / size));
|
var pitch = cellSize + gapPx;
|
||||||
return new GridMetrics(columns, rows, size);
|
if (pitch <= 0)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
var columnCount = Math.Max(1, (int)Math.Floor((availableWidth + gapPx) / pitch));
|
||||||
|
var gridWidth = columnCount * cellSize + Math.Max(0, columnCount - 1) * gapPx;
|
||||||
|
var gridHeight = rowCount * cellSize + Math.Max(0, rowCount - 1) * gapPx;
|
||||||
|
|
||||||
|
return new GridMetrics(columnCount, rowCount, cellSize, gapPx, inset, gridWidth, gridHeight);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var columnCount = shortSide;
|
||||||
|
var denominator = columnCount + Math.Max(0, columnCount - 1) * clampedGapRatio;
|
||||||
|
if (denominator <= 0)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cellSize = availableWidth / denominator;
|
||||||
|
var gapPx = cellSize * clampedGapRatio;
|
||||||
|
var pitch = cellSize + gapPx;
|
||||||
|
if (pitch <= 0)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rowCount = Math.Max(1, (int)Math.Floor((availableHeight + gapPx) / pitch));
|
||||||
|
var gridWidth = columnCount * cellSize + Math.Max(0, columnCount - 1) * gapPx;
|
||||||
|
var gridHeight = rowCount * cellSize + Math.Max(0, rowCount - 1) * gapPx;
|
||||||
|
|
||||||
|
return new GridMetrics(columnCount, rowCount, cellSize, gapPx, inset, gridWidth, gridHeight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int ClampComponentSpan(int requestedSpan, int axisCellCount)
|
private static int ClampComponentSpan(int requestedSpan, int axisCellCount)
|
||||||
@@ -537,57 +943,77 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
private void ApplyWidgetSizing(double cellSize)
|
private void ApplyWidgetSizing(double cellSize)
|
||||||
{
|
{
|
||||||
var margin = Math.Clamp(cellSize * 0.08, 1.5, 10);
|
var taskbarCellHeight = Math.Clamp(cellSize * 0.76, 36, 76);
|
||||||
var verticalPadding = Math.Clamp(cellSize * 0.08, 2, 12);
|
var taskbarTextSize = Math.Clamp(taskbarCellHeight * 0.36, 12, 22);
|
||||||
var horizontalPadding = Math.Clamp(cellSize * 0.20, 4, 22);
|
var taskbarIconSize = Math.Clamp(taskbarCellHeight * 0.46, 16, 34);
|
||||||
var taskbarCell = Math.Clamp(cellSize, 28, 128);
|
var taskbarButtonInset = Math.Clamp(taskbarCellHeight * 0.22, 6, 16);
|
||||||
var unifiedFontSize = Math.Clamp(cellSize * 0.22, 8, 22);
|
var compactButtonInset = Math.Clamp(taskbarCellHeight * 0.20, 6, 14);
|
||||||
var unifiedIconSize = Math.Clamp(cellSize * 0.28, 10, 26);
|
var buttonContentSpacing = Math.Clamp(taskbarCellHeight * 0.20, 6, 14);
|
||||||
|
var taskbarButtonPadding = new Thickness(taskbarButtonInset);
|
||||||
|
|
||||||
TopStatusBarHost.Padding = new Thickness(Math.Clamp(cellSize * 0.08, 1.5, 10));
|
// Status bar and taskbar are special surfaces: they should fill their row.
|
||||||
ClockWidget.Margin = new Thickness(margin);
|
TopStatusBarHost.Margin = new Thickness(0);
|
||||||
|
TopStatusBarHost.Padding = new Thickness(0);
|
||||||
|
|
||||||
|
BottomTaskbarContainer.Margin = new Thickness(0);
|
||||||
|
BottomTaskbarContainer.CornerRadius = new CornerRadius(Math.Clamp(taskbarCellHeight * 0.58, 20, 44));
|
||||||
|
BottomTaskbarContainer.Padding = new Thickness(Math.Clamp(taskbarCellHeight * 0.16, 6, 14));
|
||||||
|
|
||||||
|
ClockWidget.Margin = new Thickness(0);
|
||||||
ClockWidget.ApplyCellSize(cellSize);
|
ClockWidget.ApplyCellSize(cellSize);
|
||||||
|
|
||||||
BottomTaskbarContainer.Margin = new Thickness(Math.Clamp(cellSize * 0.18, 6, 18));
|
var buttonMinWidth = Math.Clamp(taskbarCellHeight * 2.35, 100, 340);
|
||||||
BottomTaskbarContainer.CornerRadius = new CornerRadius(Math.Clamp(cellSize * 0.24, 10, 24));
|
|
||||||
BottomTaskbarContainer.Padding = new Thickness(Math.Clamp(cellSize * 0.08, 2, 10));
|
|
||||||
|
|
||||||
BackToWindowsButton.Margin = new Thickness(0);
|
BackToWindowsButton.Margin = new Thickness(0);
|
||||||
BackToWindowsButton.Padding = new Thickness(horizontalPadding, verticalPadding);
|
BackToWindowsButton.Padding = taskbarButtonPadding;
|
||||||
BackToWindowsButton.FontSize = unifiedFontSize;
|
BackToWindowsButton.FontSize = taskbarTextSize;
|
||||||
BackToWindowsButton.MinHeight = taskbarCell;
|
BackToWindowsButton.MinHeight = taskbarCellHeight;
|
||||||
BackToWindowsButton.MinWidth = Math.Clamp(cellSize * 2.3, 90, 320);
|
BackToWindowsButton.MinWidth = buttonMinWidth;
|
||||||
BackToWindowsIcon.FontSize = unifiedIconSize;
|
BackToWindowsIcon.FontSize = taskbarIconSize;
|
||||||
|
BackToWindowsTextBlock.FontSize = taskbarTextSize;
|
||||||
|
SetButtonContentSpacing(BackToWindowsButton, buttonContentSpacing);
|
||||||
|
|
||||||
OpenComponentLibraryButton.Margin = new Thickness(0);
|
OpenComponentLibraryButton.Margin = new Thickness(0);
|
||||||
OpenComponentLibraryButton.Padding = new Thickness(horizontalPadding, verticalPadding);
|
OpenComponentLibraryButton.Padding = taskbarButtonPadding;
|
||||||
OpenComponentLibraryButton.FontSize = unifiedFontSize;
|
OpenComponentLibraryButton.FontSize = taskbarTextSize;
|
||||||
OpenComponentLibraryButton.MinHeight = taskbarCell;
|
OpenComponentLibraryButton.MinHeight = taskbarCellHeight;
|
||||||
OpenComponentLibraryButton.MinWidth = Math.Clamp(cellSize * 2.0, 88, 300);
|
OpenComponentLibraryButton.MinWidth = Math.Clamp(taskbarCellHeight * 2.15, 92, 320);
|
||||||
OpenComponentLibraryIcon.FontSize = unifiedIconSize;
|
OpenComponentLibraryIcon.FontSize = taskbarIconSize;
|
||||||
|
OpenComponentLibraryTextBlock.FontSize = taskbarTextSize;
|
||||||
|
SetButtonContentSpacing(OpenComponentLibraryButton, buttonContentSpacing);
|
||||||
|
|
||||||
OpenSettingsButton.Margin = new Thickness(0);
|
OpenSettingsButton.Margin = new Thickness(0);
|
||||||
OpenSettingsButton.Height = taskbarCell;
|
OpenSettingsButton.Height = taskbarCellHeight;
|
||||||
OpenSettingsButton.MinHeight = taskbarCell;
|
OpenSettingsButton.MinHeight = taskbarCellHeight;
|
||||||
OpenSettingsIcon.FontSize = unifiedIconSize;
|
OpenSettingsButton.FontSize = taskbarTextSize;
|
||||||
|
OpenSettingsButtonTextBlock.FontSize = taskbarTextSize;
|
||||||
|
OpenSettingsIcon.FontSize = taskbarIconSize;
|
||||||
|
SetButtonContentSpacing(OpenSettingsButton, Math.Clamp(taskbarCellHeight * 0.18, 4, 10));
|
||||||
|
|
||||||
if (_isSettingsOpen)
|
if (_isSettingsOpen)
|
||||||
{
|
{
|
||||||
OpenSettingsButton.Width = double.NaN;
|
OpenSettingsButton.Width = double.NaN;
|
||||||
OpenSettingsButton.MinWidth = Math.Clamp(cellSize * 2.3, 120, 340);
|
OpenSettingsButton.MinWidth = Math.Clamp(taskbarCellHeight * 2.45, 120, 360);
|
||||||
OpenSettingsButton.Padding = new Thickness(horizontalPadding, verticalPadding);
|
OpenSettingsButton.Padding = taskbarButtonPadding;
|
||||||
OpenSettingsButton.FontSize = unifiedFontSize;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
OpenSettingsButton.Width = taskbarCell;
|
OpenSettingsButton.Width = taskbarCellHeight;
|
||||||
OpenSettingsButton.MinWidth = taskbarCell;
|
OpenSettingsButton.MinWidth = taskbarCellHeight;
|
||||||
OpenSettingsButton.Padding = new Thickness(Math.Clamp(taskbarCell * 0.2, 4, 12));
|
OpenSettingsButton.Padding = new Thickness(compactButtonInset);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateComponentLibraryLayout(cellSize);
|
UpdateComponentLibraryLayout(cellSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void SetButtonContentSpacing(Button? button, double spacing)
|
||||||
|
{
|
||||||
|
if (button?.Content is StackPanel contentPanel)
|
||||||
|
{
|
||||||
|
contentPanel.Spacing = spacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateComponentLibraryLayout(double cellSize)
|
private void UpdateComponentLibraryLayout(double cellSize)
|
||||||
{
|
{
|
||||||
if (ComponentLibraryWindow is null)
|
if (ComponentLibraryWindow is null)
|
||||||
@@ -597,8 +1023,14 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
var horizontalMargin = Math.Clamp(cellSize * 0.7, 18, 44);
|
var horizontalMargin = Math.Clamp(cellSize * 0.7, 18, 44);
|
||||||
var bottomMargin = Math.Clamp(cellSize * 1.4, 56, 190);
|
var bottomMargin = Math.Clamp(cellSize * 1.4, 56, 190);
|
||||||
ComponentLibraryWindow.Margin = new Thickness(horizontalMargin, 20, horizontalMargin, bottomMargin);
|
var defaultMargin = new Thickness(horizontalMargin, 20, horizontalMargin, bottomMargin);
|
||||||
ComponentLibraryWindow.CornerRadius = new CornerRadius(Math.Clamp(cellSize * 0.24, 12, 24));
|
if (!_isComponentLibraryWindowPositionCustomized)
|
||||||
|
{
|
||||||
|
_savedComponentLibraryMargin = defaultMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentLibraryWindow.Margin = _savedComponentLibraryMargin;
|
||||||
|
ComponentLibraryWindow.CornerRadius = new CornerRadius(Math.Clamp(cellSize * 0.45, 24, 44));
|
||||||
ComponentLibraryWindow.Height = Math.Clamp(cellSize * 4.8, 220, 360);
|
ComponentLibraryWindow.Height = Math.Clamp(cellSize * 4.8, 220, 360);
|
||||||
ComponentLibraryWindow.Width = Math.Clamp(cellSize * 9.2, 360, 760);
|
ComponentLibraryWindow.Width = Math.Clamp(cellSize * 9.2, 360, 760);
|
||||||
}
|
}
|
||||||
@@ -613,10 +1045,26 @@ public partial class MainWindow : Window
|
|||||||
var clampedCell = Math.Max(1, cellSize);
|
var clampedCell = Math.Max(1, cellSize);
|
||||||
var horizontalInset = Math.Clamp(clampedCell * 0.45, 12, 64);
|
var horizontalInset = Math.Clamp(clampedCell * 0.45, 12, 64);
|
||||||
var verticalGap = Math.Clamp(clampedCell * 0.16, 6, 18);
|
var verticalGap = Math.Clamp(clampedCell * 0.16, 6, 18);
|
||||||
var topInset = clampedCell + verticalGap;
|
var edgeInset = Math.Max(0, _currentDesktopEdgeInset);
|
||||||
var bottomInset = clampedCell + verticalGap;
|
|
||||||
|
|
||||||
// 添加额外的安全边距以确保圆角不被裁剪
|
var taskbarCellHeight = Math.Clamp(clampedCell * 0.76, 36, 76);
|
||||||
|
var taskbarPadding = Math.Clamp(taskbarCellHeight * 0.16, 6, 14);
|
||||||
|
var taskbarVisualHeight = Math.Max(clampedCell, taskbarCellHeight + taskbarPadding * 2);
|
||||||
|
if (BottomTaskbarContainer is not null && BottomTaskbarContainer.Bounds.Height > 1)
|
||||||
|
{
|
||||||
|
taskbarVisualHeight = Math.Max(taskbarVisualHeight, BottomTaskbarContainer.Bounds.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
var statusBarVisualHeight = clampedCell;
|
||||||
|
if (TopStatusBarHost is not null && TopStatusBarHost.Bounds.Height > 1)
|
||||||
|
{
|
||||||
|
statusBarVisualHeight = Math.Max(statusBarVisualHeight, TopStatusBarHost.Bounds.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
var topInset = Math.Max(clampedCell + verticalGap, edgeInset + statusBarVisualHeight + verticalGap);
|
||||||
|
var bottomInset = Math.Max(clampedCell + verticalGap, edgeInset + taskbarVisualHeight + verticalGap);
|
||||||
|
|
||||||
|
// Add extra safety margin so rounded panel corners never clip against viewport edges.
|
||||||
var cornerSafetyMargin = Math.Clamp(clampedCell * 0.12, 4, 12);
|
var cornerSafetyMargin = Math.Clamp(clampedCell * 0.12, 4, 12);
|
||||||
var inset = new Thickness(
|
var inset = new Thickness(
|
||||||
horizontalInset + cornerSafetyMargin,
|
horizontalInset + cornerSafetyMargin,
|
||||||
@@ -624,8 +1072,7 @@ public partial class MainWindow : Window
|
|||||||
horizontalInset + cornerSafetyMargin,
|
horizontalInset + cornerSafetyMargin,
|
||||||
bottomInset + cornerSafetyMargin);
|
bottomInset + cornerSafetyMargin);
|
||||||
|
|
||||||
// 使用 Margin 来定位,而不是直接设置 Width/Height
|
// Keep panel stretched with explicit viewport insets so it never overlaps fixed chrome.
|
||||||
// 这样可以让面板自然填充可用空间,同时保持边距
|
|
||||||
SettingsContentPanel.HorizontalAlignment = HorizontalAlignment.Stretch;
|
SettingsContentPanel.HorizontalAlignment = HorizontalAlignment.Stretch;
|
||||||
SettingsContentPanel.VerticalAlignment = VerticalAlignment.Stretch;
|
SettingsContentPanel.VerticalAlignment = VerticalAlignment.Stretch;
|
||||||
SettingsContentPanel.Margin = inset;
|
SettingsContentPanel.Margin = inset;
|
||||||
@@ -656,29 +1103,46 @@ public partial class MainWindow : Window
|
|||||||
var aspectRatio = desktopWidth / desktopHeight;
|
var aspectRatio = desktopWidth / desktopHeight;
|
||||||
|
|
||||||
var availableWidth = Math.Max(100, WallpaperPreviewHost.Bounds.Width);
|
var availableWidth = Math.Max(100, WallpaperPreviewHost.Bounds.Width);
|
||||||
|
var availableHeight = WallpaperPreviewHost.Bounds.Height;
|
||||||
|
// During initial measure, host height can be too small and cause the preview to collapse.
|
||||||
|
// Ignore tiny heights so width-driven sizing can stabilize first.
|
||||||
|
if (availableHeight < 120)
|
||||||
|
{
|
||||||
|
availableHeight = double.PositiveInfinity;
|
||||||
|
}
|
||||||
|
|
||||||
var framePadding = WallpaperPreviewFrame.Padding;
|
var framePadding = WallpaperPreviewFrame.Padding;
|
||||||
var horizontalPadding = framePadding.Left + framePadding.Right;
|
var horizontalPadding = framePadding.Left + framePadding.Right;
|
||||||
var verticalPadding = framePadding.Top + framePadding.Bottom;
|
var verticalPadding = framePadding.Top + framePadding.Bottom;
|
||||||
|
|
||||||
var previewWidth = availableWidth;
|
var previewWidth = Math.Min(availableWidth, WallpaperPreviewMaxWidth);
|
||||||
var previewHeight = previewWidth / aspectRatio;
|
var previewHeight = previewWidth / aspectRatio;
|
||||||
|
if (double.IsFinite(availableHeight) && previewHeight > availableHeight)
|
||||||
|
{
|
||||||
|
previewHeight = availableHeight;
|
||||||
|
previewWidth = previewHeight * aspectRatio;
|
||||||
|
}
|
||||||
|
|
||||||
WallpaperPreviewFrame.Width = previewWidth;
|
WallpaperPreviewFrame.Width = previewWidth;
|
||||||
WallpaperPreviewFrame.Height = previewHeight;
|
WallpaperPreviewFrame.Height = previewHeight;
|
||||||
|
|
||||||
WallpaperPreviewClockTextBlock.Text = DateTime.Now.ToString("HH:mm");
|
|
||||||
|
|
||||||
var innerWidth = Math.Max(1, previewWidth - horizontalPadding);
|
var innerWidth = Math.Max(1, previewWidth - horizontalPadding);
|
||||||
var innerHeight = Math.Max(1, previewHeight - verticalPadding);
|
var innerHeight = Math.Max(1, previewHeight - verticalPadding);
|
||||||
var gridMetrics = CalculateGridMetrics(innerWidth, innerHeight, _targetShortSideCells);
|
var gapRatio = ResolveGridGapRatio(_gridSpacingPreset);
|
||||||
|
var edgeInset = CalculateEdgeInset(innerWidth, innerHeight, _targetShortSideCells, _desktopEdgeInsetPercent);
|
||||||
|
var gridMetrics = CalculateGridMetrics(innerWidth, innerHeight, _targetShortSideCells, gapRatio, edgeInset);
|
||||||
if (gridMetrics.CellSize <= 0)
|
if (gridMetrics.CellSize <= 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WallpaperPreviewGrid.Width = gridMetrics.ColumnCount * gridMetrics.CellSize;
|
WallpaperPreviewGrid.Margin = new Thickness(gridMetrics.EdgeInsetPx);
|
||||||
WallpaperPreviewGrid.Height = gridMetrics.RowCount * gridMetrics.CellSize;
|
WallpaperPreviewGrid.RowSpacing = gridMetrics.GapPx;
|
||||||
|
WallpaperPreviewGrid.ColumnSpacing = gridMetrics.GapPx;
|
||||||
|
WallpaperPreviewGrid.Width = gridMetrics.GridWidthPx;
|
||||||
|
WallpaperPreviewGrid.Height = gridMetrics.GridHeightPx;
|
||||||
|
|
||||||
// This can be triggered by layout changes; always rebuild the preview grid definitions
|
// This can be triggered by layout changes; always rebuild the preview grid definitions
|
||||||
// to avoid definitions accumulating and shifting overlay components out of place.
|
// to avoid definitions accumulating and shifting overlay components out of place.
|
||||||
@@ -712,6 +1176,7 @@ public partial class MainWindow : Window
|
|||||||
ApplyTopStatusComponentVisibility();
|
ApplyTopStatusComponentVisibility();
|
||||||
ApplyTaskbarActionVisibility(GetCurrentTaskbarContext());
|
ApplyTaskbarActionVisibility(GetCurrentTaskbarContext());
|
||||||
ApplyPreviewWidgetSizing(gridMetrics.CellSize);
|
ApplyPreviewWidgetSizing(gridMetrics.CellSize);
|
||||||
|
ApplyStatusBarComponentSpacingForPanel(WallpaperPreviewTopStatusComponentsPanel, gridMetrics.CellSize);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -721,22 +1186,33 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
private void ApplyPreviewWidgetSizing(double cellSize)
|
private void ApplyPreviewWidgetSizing(double cellSize)
|
||||||
{
|
{
|
||||||
var margin = Math.Clamp(cellSize * 0.08, 1, 6);
|
var previewTaskbarCell = Math.Clamp(cellSize * 0.74, 10, 28);
|
||||||
var previewTaskbarCell = Math.Clamp(cellSize, 10, 36);
|
var previewTextSize = Math.Clamp(previewTaskbarCell * 0.38, 7, 14);
|
||||||
WallpaperPreviewTopStatusBarHost.Padding = new Thickness(Math.Clamp(cellSize * 0.08, 1, 4));
|
var previewIconSize = Math.Clamp(previewTaskbarCell * 0.46, 8, 16);
|
||||||
WallpaperPreviewBottomTaskbarContainer.Margin = new Thickness(margin);
|
var previewInset = Math.Clamp(previewTaskbarCell * 0.20, 2, 6);
|
||||||
WallpaperPreviewBottomTaskbarContainer.CornerRadius = new CornerRadius(Math.Clamp(cellSize * 0.22, 4, 10));
|
var previewContentSpacing = Math.Clamp(previewTaskbarCell * 0.20, 2, 6);
|
||||||
WallpaperPreviewBottomTaskbarContainer.Padding = new Thickness(Math.Clamp(cellSize * 0.06, 1, 4));
|
|
||||||
|
// Match desktop behavior: special bars fill their preview row.
|
||||||
|
WallpaperPreviewTopStatusBarHost.Margin = new Thickness(0);
|
||||||
|
WallpaperPreviewTopStatusBarHost.Padding = new Thickness(0);
|
||||||
|
|
||||||
WallpaperPreviewClockTextBlock.FontSize = Math.Clamp(cellSize * 0.30, 6, 18);
|
WallpaperPreviewBottomTaskbarContainer.Margin = new Thickness(0);
|
||||||
WallpaperPreviewBackButtonTextBlock.FontSize = Math.Clamp(cellSize * 0.19, 5, 13);
|
WallpaperPreviewBottomTaskbarContainer.CornerRadius = new CornerRadius(Math.Clamp(cellSize * 0.45, 6, 14));
|
||||||
WallpaperPreviewComponentLibraryTextBlock.FontSize = Math.Clamp(cellSize * 0.18, 5, 12);
|
WallpaperPreviewBottomTaskbarContainer.Padding = new Thickness(previewInset);
|
||||||
|
|
||||||
|
WallpaperPreviewClockWidget.ApplyCellSize(cellSize);
|
||||||
|
WallpaperPreviewBackButtonTextBlock.FontSize = previewTextSize;
|
||||||
|
WallpaperPreviewComponentLibraryTextBlock.FontSize = previewTextSize;
|
||||||
|
WallpaperPreviewBackButtonVisual.Spacing = previewContentSpacing;
|
||||||
|
WallpaperPreviewComponentLibraryVisual.Spacing = previewContentSpacing;
|
||||||
|
|
||||||
WallpaperPreviewBackButtonVisual.MinHeight = previewTaskbarCell;
|
WallpaperPreviewBackButtonVisual.MinHeight = previewTaskbarCell;
|
||||||
WallpaperPreviewBackButtonVisual.MinWidth = Math.Clamp(cellSize * 2.1, 30, 120);
|
WallpaperPreviewBackButtonVisual.MinWidth = Math.Clamp(cellSize * 2.1, 30, 120);
|
||||||
WallpaperPreviewComponentLibraryVisual.MinHeight = previewTaskbarCell;
|
WallpaperPreviewComponentLibraryVisual.MinHeight = previewTaskbarCell;
|
||||||
WallpaperPreviewComponentLibraryVisual.MinWidth = Math.Clamp(cellSize * 2.0, 28, 110);
|
WallpaperPreviewComponentLibraryVisual.MinWidth = Math.Clamp(cellSize * 2.0, 28, 110);
|
||||||
WallpaperPreviewSettingsButtonIcon.Width = Math.Clamp(previewTaskbarCell * 0.42, 6, 14);
|
|
||||||
WallpaperPreviewSettingsButtonIcon.Height = Math.Clamp(previewTaskbarCell * 0.42, 6, 14);
|
WallpaperPreviewSettingsButtonIcon.Width = previewIconSize;
|
||||||
|
WallpaperPreviewSettingsButtonIcon.Height = previewIconSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMinimizeClick(object? sender, RoutedEventArgs e)
|
private void OnMinimizeClick(object? sender, RoutedEventArgs e)
|
||||||
@@ -767,7 +1243,7 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
private void InitializeTimeZoneSettings()
|
private void InitializeTimeZoneSettings()
|
||||||
{
|
{
|
||||||
// 填充时区下拉框
|
// Populate timezone dropdown items before selecting current timezone.
|
||||||
TimeZoneComboBox.Items.Clear();
|
TimeZoneComboBox.Items.Clear();
|
||||||
var timeZones = _timeZoneService.GetAllTimeZones();
|
var timeZones = _timeZoneService.GetAllTimeZones();
|
||||||
foreach (var tz in timeZones)
|
foreach (var tz in timeZones)
|
||||||
@@ -780,7 +1256,7 @@ public partial class MainWindow : Window
|
|||||||
};
|
};
|
||||||
TimeZoneComboBox.Items.Add(item);
|
TimeZoneComboBox.Items.Add(item);
|
||||||
|
|
||||||
// 选中当前时区
|
// 閫変腑褰撳墠鏃跺尯
|
||||||
if (tz.Id == _timeZoneService.CurrentTimeZone.Id)
|
if (tz.Id == _timeZoneService.CurrentTimeZone.Id)
|
||||||
{
|
{
|
||||||
TimeZoneComboBox.SelectedItem = item;
|
TimeZoneComboBox.SelectedItem = item;
|
||||||
@@ -805,3 +1281,4 @@ public partial class MainWindow : Window
|
|||||||
PersistSettings();
|
PersistSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
177
docs/CORNER_RADIUS_SPEC.md
Normal file
177
docs/CORNER_RADIUS_SPEC.md
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
# 圆角设计规范 (Corner Radius Design System)
|
||||||
|
|
||||||
|
> 基于小米澎湃OS 3 (HyperOS) 设计语言
|
||||||
|
|
||||||
|
## 设计理念
|
||||||
|
|
||||||
|
澎湃OS 3 采用**"生命感美学"**设计语言,强调:
|
||||||
|
- **全局圆角设计** - 所有界面元素均采用圆角
|
||||||
|
- **视觉舒适统一** - 柔和、现代、细腻
|
||||||
|
- **多级渲染** - 配合模糊混色与阴影
|
||||||
|
- **层级分明** - 大容器使用大圆角,小元素使用小圆角
|
||||||
|
|
||||||
|
## 圆角数值体系
|
||||||
|
|
||||||
|
### 核心数值
|
||||||
|
|
||||||
|
| 级别 | 圆角值 (px) | 用途 |
|
||||||
|
|------|-------------|------|
|
||||||
|
| **Level 0** | 0 | 特殊场景(无圆角需求) |
|
||||||
|
| **Level 1** | 12 | 小元素、图标内边角、ListBoxItem |
|
||||||
|
| **Level 2** | 16 | 色块按钮、小组件 |
|
||||||
|
| **Level 3** | 20 | 普通按钮、组件预览 |
|
||||||
|
| **Level 4** | 24 | 输入框、小型面板 |
|
||||||
|
| **Level 5** | 28 | 面板/卡片 (glass-panel) |
|
||||||
|
| **Level 6** | 32 | Mica 风格面板 (mica-strong) |
|
||||||
|
| **Level 7** | 36 | 大容器 (glass-strong)、任务栏、窗口 |
|
||||||
|
|
||||||
|
### 动态圆角
|
||||||
|
|
||||||
|
动态圆角根据格子大小(cellSize)动态计算:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 小元素
|
||||||
|
CornerRadius = Math.Clamp(cellSize * 0.35, 16, 28);
|
||||||
|
|
||||||
|
// 小组件
|
||||||
|
CornerRadius = Math.Clamp(cellSize * 0.45, 24, 44);
|
||||||
|
|
||||||
|
// 大容器(任务栏/窗口)
|
||||||
|
CornerRadius = Math.Clamp(cellSize * 0.45, 24, 44);
|
||||||
|
```
|
||||||
|
|
||||||
|
**系数参考**:
|
||||||
|
- 系数范围:`0.35 - 0.45`
|
||||||
|
- 最小值限制:`12 - 24 px`
|
||||||
|
- 最大值限制:`28 - 44 px`
|
||||||
|
|
||||||
|
## 组件圆角速查表
|
||||||
|
|
||||||
|
### 基础控件
|
||||||
|
|
||||||
|
| 控件 | 圆角值 | 代码位置 |
|
||||||
|
|------|--------|---------|
|
||||||
|
| Button | 20px | GlassModule.axaml |
|
||||||
|
| ToggleSwitch | 继承系统 | - |
|
||||||
|
| TextBox | 20px | glass-panel |
|
||||||
|
| ComboBox | 20px | glass-panel |
|
||||||
|
| NumberBox | 20px | glass-panel |
|
||||||
|
|
||||||
|
### 容器样式类
|
||||||
|
|
||||||
|
| 样式类 | 圆角值 | 说明 |
|
||||||
|
|--------|--------|------|
|
||||||
|
| `.glass-panel` | 28px | 普通玻璃面板 |
|
||||||
|
| `.glass-strong` | 36px | 加强玻璃面板(任务栏) |
|
||||||
|
| `.mica-strong` | 36px | Mica 风格面板(设置页) |
|
||||||
|
| `.glass-overlay` | 0px | 覆盖层(无圆角) |
|
||||||
|
|
||||||
|
### 特殊场景
|
||||||
|
|
||||||
|
| 场景 | 圆角值 | 说明 |
|
||||||
|
|------|--------|------|
|
||||||
|
| 窗口整体 | 36px | 组件库/设置窗口 |
|
||||||
|
| 窗口标题栏 | 36px | 仅顶部圆角 (`36,36,0,0`) |
|
||||||
|
| 颜色选择器色块 | 12px | Monet 颜色/推荐色 |
|
||||||
|
| 设置页 ListBoxItem | 12px | 导航项 |
|
||||||
|
| 预览视口 | 12-16px | 壁纸/网格预览 |
|
||||||
|
|
||||||
|
## 圆角层级视觉示例
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ Level 7: 大容器 (36px) │
|
||||||
|
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ Level 6: Mica 面板 (36px) │ │
|
||||||
|
│ │ ┌─────────────────────────────────────────────────────┐ │ │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
│ │ │ Level 5: 玻璃面板 (28px) │ │ │
|
||||||
|
│ │ │ ┌─────────────────────────────────────────────┐ │ │ │
|
||||||
|
│ │ │ │ │ │ │ │
|
||||||
|
│ │ │ │ Level 4: 输入面板 (24px) │ │ │ │
|
||||||
|
│ │ │ │ ┌─────────────────────────────────────┐ │ │ │ │
|
||||||
|
│ │ │ │ │ │ │ │ │ │
|
||||||
|
│ │ │ │ │ Level 3: 按钮 (20px) │ │ │ │ │
|
||||||
|
│ │ │ │ │ ┌─────────────────────────────┐ │ │ │ │ │
|
||||||
|
│ │ │ │ │ │ Level 2: 色块 (16px) │ │ │ │ │ │
|
||||||
|
│ │ │ │ │ │ ┌─────────────────────┐ │ │ │ │ │ │
|
||||||
|
│ │ │ │ │ │ │ Level 1: 小元素 │ │ │ │ │ │ │
|
||||||
|
│ │ │ │ │ │ │ (12px) │ │ │ │ │ │ │
|
||||||
|
│ │ │ │ │ │ └─────────────────────┘ │ │ │ │ │ │
|
||||||
|
│ │ │ │ │ └─────────────────────────────┘ │ │ │ │ │
|
||||||
|
│ │ │ │ └─────────────────────────────────────┘ │ │ │ │
|
||||||
|
│ │ │ └─────────────────────────────────────────────┘ │ │ │
|
||||||
|
│ │ └─────────────────────────────────────────────────────┘ │ │
|
||||||
|
│ └───────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 在 XAML 中使用
|
||||||
|
|
||||||
|
### 直接使用固定值
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<Border Classes="glass-strong" CornerRadius="36">
|
||||||
|
<!-- 内容 -->
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Button CornerRadius="20">点击</Button>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用样式类
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- 使用预定义的 glass-panel 样式 -->
|
||||||
|
<Border Classes="glass-panel">
|
||||||
|
<TextBlock Text="面板内容" />
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- 组合多个样式类 -->
|
||||||
|
<Border Classes="glass-strong mica-strong">
|
||||||
|
<!-- 内容 -->
|
||||||
|
</Border>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 动态圆角(Code-Behind)
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 根据格子大小动态计算圆角
|
||||||
|
var cellSize = 100; // 假设格子大小
|
||||||
|
var cornerRadius = Math.Clamp(cellSize * 0.45, 24, 44);
|
||||||
|
|
||||||
|
BottomTaskbarContainer.CornerRadius = new CornerRadius(cornerRadius);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 新增控件时的圆角规范
|
||||||
|
|
||||||
|
1. **确定元素层级** - 根据容器大小选择合适的级别
|
||||||
|
2. **遵循视觉一致性** - 同层级的元素使用相同圆角
|
||||||
|
3. **考虑内容安全区** - 圆角不应遮挡重要内容
|
||||||
|
4. **响应式适配** - 大屏幕使用较大圆角,小屏幕使用较小圆角
|
||||||
|
|
||||||
|
### 快速参考
|
||||||
|
|
||||||
|
```
|
||||||
|
新控件圆角选择流程:
|
||||||
|
|
||||||
|
1. 是窗口/大容器? → Level 7 (36px)
|
||||||
|
2. 是面板/卡片? → Level 5-6 (28-36px)
|
||||||
|
3. 是按钮/输入框? → Level 3-4 (20-24px)
|
||||||
|
4. 是小组件/色块? → Level 2 (16px)
|
||||||
|
5. 是图标/小元素? → Level 1 (12px)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 附录:修改历史
|
||||||
|
|
||||||
|
| 日期 | 修改人 | 说明 |
|
||||||
|
|------|--------|------|
|
||||||
|
| 2026-03-02 | AI Assistant | 初始规范,基于澎湃OS 3 设计语言 |
|
||||||
|
|
||||||
|
## 参考资料
|
||||||
|
|
||||||
|
- 澎湃OS 生命感美学设计
|
||||||
|
- Xiaomi HyperOS Design Guidelines
|
||||||
|
- 小米小部件审核规范 (dev.mi.com)
|
||||||
Reference in New Issue
Block a user