mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 23:54:26 +08:00
0.2.4
稳定性提升
This commit is contained in:
@@ -92,7 +92,8 @@ public sealed class ComponentRegistry
|
||||
MinWidthCells: 2,
|
||||
MinHeightCells: 4,
|
||||
AllowStatusBarPlacement: false,
|
||||
AllowDesktopPlacement: true),
|
||||
AllowDesktopPlacement: true,
|
||||
ResizeMode: DesktopComponentResizeMode.Free),
|
||||
new DesktopComponentDefinition(
|
||||
BuiltInComponentIds.DesktopBlackboardLandscape,
|
||||
"Blackboard Landscape",
|
||||
@@ -101,7 +102,8 @@ public sealed class ComponentRegistry
|
||||
MinWidthCells: 4,
|
||||
MinHeightCells: 2,
|
||||
AllowStatusBarPlacement: false,
|
||||
AllowDesktopPlacement: true),
|
||||
AllowDesktopPlacement: true,
|
||||
ResizeMode: DesktopComponentResizeMode.Free),
|
||||
new DesktopComponentDefinition(
|
||||
BuiltInComponentIds.Date,
|
||||
"Calendar",
|
||||
|
||||
@@ -8,4 +8,5 @@ public sealed record DesktopComponentDefinition(
|
||||
int MinWidthCells,
|
||||
int MinHeightCells,
|
||||
bool AllowStatusBarPlacement,
|
||||
bool AllowDesktopPlacement);
|
||||
bool AllowDesktopPlacement,
|
||||
DesktopComponentResizeMode ResizeMode = DesktopComponentResizeMode.Proportional);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace LanMontainDesktop.ComponentSystem;
|
||||
|
||||
public enum DesktopComponentResizeMode
|
||||
{
|
||||
Proportional = 0,
|
||||
Free = 1
|
||||
}
|
||||
@@ -69,7 +69,7 @@ public sealed class TimeZoneService
|
||||
/// </summary>
|
||||
public string GetTimeZoneDisplayName(TimeZoneInfo timeZone)
|
||||
{
|
||||
var offset = timeZone.BaseUtcOffset;
|
||||
var offset = timeZone.GetUtcOffset(DateTime.UtcNow);
|
||||
var sign = offset >= TimeSpan.Zero ? "+" : "-";
|
||||
var hours = Math.Abs(offset.Hours);
|
||||
var minutes = Math.Abs(offset.Minutes);
|
||||
|
||||
@@ -82,7 +82,7 @@ public sealed class WindowsStartMenuService
|
||||
nameOverride ?? Path.GetFileName(folderPath),
|
||||
relativePath);
|
||||
|
||||
foreach (var subFolderPath in Directory.EnumerateDirectories(folderPath))
|
||||
foreach (var subFolderPath in EnumerateDirectoriesSafe(folderPath))
|
||||
{
|
||||
var folderName = Path.GetFileName(subFolderPath);
|
||||
if (folderName.StartsWith(".", StringComparison.Ordinal))
|
||||
@@ -90,38 +90,76 @@ public sealed class WindowsStartMenuService
|
||||
continue;
|
||||
}
|
||||
|
||||
folder.Folders.Add(ScanFolder(subFolderPath, rootPath));
|
||||
try
|
||||
{
|
||||
folder.Folders.Add(ScanFolder(subFolderPath, rootPath));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Skip unreadable branches but continue scanning siblings.
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var filePath in Directory.EnumerateFiles(folderPath))
|
||||
foreach (var filePath in EnumerateFilesSafe(folderPath))
|
||||
{
|
||||
var extension = Path.GetExtension(filePath);
|
||||
if (!SupportedEntryExtensions.Contains(extension))
|
||||
try
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var extension = Path.GetExtension(filePath);
|
||||
if (!SupportedEntryExtensions.Contains(extension))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var fileName = Path.GetFileNameWithoutExtension(filePath);
|
||||
if (string.IsNullOrWhiteSpace(fileName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var fileName = Path.GetFileNameWithoutExtension(filePath);
|
||||
if (string.IsNullOrWhiteSpace(fileName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var normalizedName = fileName.Replace('_', ' ').Trim();
|
||||
folder.Apps.Add(new StartMenuAppEntry
|
||||
var normalizedName = fileName.Replace('_', ' ').Trim();
|
||||
folder.Apps.Add(new StartMenuAppEntry
|
||||
{
|
||||
DisplayName = normalizedName,
|
||||
FilePath = filePath,
|
||||
RelativePath = Path.GetRelativePath(rootPath, filePath),
|
||||
IconPngBytes = OperatingSystem.IsWindows()
|
||||
? WindowsIconService.TryGetIconPngBytes(filePath)
|
||||
: null
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
DisplayName = normalizedName,
|
||||
FilePath = filePath,
|
||||
RelativePath = Path.GetRelativePath(rootPath, filePath),
|
||||
IconPngBytes = OperatingSystem.IsWindows()
|
||||
? WindowsIconService.TryGetIconPngBytes(filePath)
|
||||
: null
|
||||
});
|
||||
// Skip unreadable or invalid entries and continue.
|
||||
}
|
||||
}
|
||||
|
||||
return folder;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> EnumerateDirectoriesSafe(string folderPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Directory.EnumerateDirectories(folderPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<string> EnumerateFilesSafe(string folderPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Directory.EnumerateFiles(folderPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
}
|
||||
|
||||
private static void MergeFolder(StartMenuFolderNode target, StartMenuFolderNode source)
|
||||
{
|
||||
var appPathSet = new HashSet<string>(
|
||||
|
||||
@@ -45,16 +45,23 @@ public partial class AnalogClockWidget : UserControl, IDesktopComponentWidget, I
|
||||
|
||||
public void SetTimeZoneService(TimeZoneService timeZoneService)
|
||||
{
|
||||
if (_timeZoneService is not null)
|
||||
{
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
}
|
||||
|
||||
ClearTimeZoneService();
|
||||
_timeZoneService = timeZoneService;
|
||||
_timeZoneService.TimeZoneChanged += OnTimeZoneChanged;
|
||||
UpdateClock();
|
||||
}
|
||||
|
||||
public void ClearTimeZoneService()
|
||||
{
|
||||
if (_timeZoneService is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
_timeZoneService = null;
|
||||
}
|
||||
|
||||
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
InitializeDialIfNeeded();
|
||||
|
||||
@@ -51,16 +51,23 @@ public partial class ClockWidget : UserControl, IDesktopComponentWidget, ITimeZo
|
||||
|
||||
public void SetTimeZoneService(TimeZoneService timeZoneService)
|
||||
{
|
||||
if (_timeZoneService != null)
|
||||
{
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
}
|
||||
|
||||
ClearTimeZoneService();
|
||||
_timeZoneService = timeZoneService;
|
||||
_timeZoneService.TimeZoneChanged += OnTimeZoneChanged;
|
||||
UpdateClock();
|
||||
}
|
||||
|
||||
public void ClearTimeZoneService()
|
||||
{
|
||||
if (_timeZoneService is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
_timeZoneService = null;
|
||||
}
|
||||
|
||||
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
UpdateClock();
|
||||
|
||||
@@ -109,16 +109,23 @@ public partial class DateWidget : UserControl, IDesktopComponentWidget, ITimeZon
|
||||
|
||||
public void SetTimeZoneService(TimeZoneService timeZoneService)
|
||||
{
|
||||
if (_timeZoneService is not null)
|
||||
{
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
}
|
||||
|
||||
ClearTimeZoneService();
|
||||
_timeZoneService = timeZoneService;
|
||||
_timeZoneService.TimeZoneChanged += OnTimeZoneChanged;
|
||||
UpdateDate();
|
||||
}
|
||||
|
||||
public void ClearTimeZoneService()
|
||||
{
|
||||
if (_timeZoneService is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
_timeZoneService = null;
|
||||
}
|
||||
|
||||
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
UpdateDate();
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
<Border Background="{DynamicResource AdaptiveBackgroundBrush}"
|
||||
Padding="24">
|
||||
<StackPanel Spacing="16">
|
||||
<TextBlock Text="日历组件设置"
|
||||
<TextBlock Text="Date Widget Settings"
|
||||
FontSize="20"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
|
||||
@@ -37,16 +37,23 @@ public partial class HolidayCalendarWidget : UserControl, IDesktopComponentWidge
|
||||
|
||||
public void SetTimeZoneService(TimeZoneService timeZoneService)
|
||||
{
|
||||
if (_timeZoneService is not null)
|
||||
{
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
}
|
||||
|
||||
ClearTimeZoneService();
|
||||
_timeZoneService = timeZoneService;
|
||||
_timeZoneService.TimeZoneChanged += OnTimeZoneChanged;
|
||||
TriggerContentRefresh();
|
||||
}
|
||||
|
||||
public void ClearTimeZoneService()
|
||||
{
|
||||
if (_timeZoneService is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
_timeZoneService = null;
|
||||
}
|
||||
|
||||
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
TriggerContentRefresh();
|
||||
|
||||
@@ -195,15 +195,22 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
|
||||
|
||||
public void SetTimeZoneService(TimeZoneService timeZoneService)
|
||||
{
|
||||
if (_timeZoneService is not null)
|
||||
{
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
}
|
||||
|
||||
ClearTimeZoneService();
|
||||
_timeZoneService = timeZoneService;
|
||||
_timeZoneService.TimeZoneChanged += OnTimeZoneChanged;
|
||||
}
|
||||
|
||||
public void ClearTimeZoneService()
|
||||
{
|
||||
if (_timeZoneService is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
_timeZoneService = null;
|
||||
}
|
||||
|
||||
public void SetWeatherInfoService(IWeatherInfoService weatherInfoService)
|
||||
{
|
||||
_weatherInfoService = weatherInfoService ?? DefaultWeatherInfoService;
|
||||
|
||||
@@ -10,6 +10,7 @@ public interface IDesktopComponentWidget
|
||||
public interface ITimeZoneAwareComponentWidget
|
||||
{
|
||||
void SetTimeZoneService(TimeZoneService timeZoneService);
|
||||
void ClearTimeZoneService();
|
||||
}
|
||||
|
||||
public interface IWeatherInfoAwareComponentWidget
|
||||
|
||||
@@ -99,16 +99,23 @@ public partial class LunarCalendarWidget : UserControl, IDesktopComponentWidget,
|
||||
|
||||
public void SetTimeZoneService(TimeZoneService timeZoneService)
|
||||
{
|
||||
if (_timeZoneService is not null)
|
||||
{
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
}
|
||||
|
||||
ClearTimeZoneService();
|
||||
_timeZoneService = timeZoneService;
|
||||
_timeZoneService.TimeZoneChanged += OnTimeZoneChanged;
|
||||
UpdateContent();
|
||||
}
|
||||
|
||||
public void ClearTimeZoneService()
|
||||
{
|
||||
if (_timeZoneService is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
_timeZoneService = null;
|
||||
}
|
||||
|
||||
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
UpdateContent();
|
||||
|
||||
@@ -40,16 +40,23 @@ public partial class MonthCalendarWidget : UserControl, IDesktopComponentWidget,
|
||||
|
||||
public void SetTimeZoneService(TimeZoneService timeZoneService)
|
||||
{
|
||||
if (_timeZoneService is not null)
|
||||
{
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
}
|
||||
|
||||
ClearTimeZoneService();
|
||||
_timeZoneService = timeZoneService;
|
||||
_timeZoneService.TimeZoneChanged += OnTimeZoneChanged;
|
||||
UpdateCalendar();
|
||||
}
|
||||
|
||||
public void ClearTimeZoneService()
|
||||
{
|
||||
if (_timeZoneService is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
_timeZoneService = null;
|
||||
}
|
||||
|
||||
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
UpdateCalendar();
|
||||
|
||||
@@ -195,15 +195,22 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
|
||||
|
||||
public void SetTimeZoneService(TimeZoneService timeZoneService)
|
||||
{
|
||||
if (_timeZoneService is not null)
|
||||
{
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
}
|
||||
|
||||
ClearTimeZoneService();
|
||||
_timeZoneService = timeZoneService;
|
||||
_timeZoneService.TimeZoneChanged += OnTimeZoneChanged;
|
||||
}
|
||||
|
||||
public void ClearTimeZoneService()
|
||||
{
|
||||
if (_timeZoneService is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
_timeZoneService = null;
|
||||
}
|
||||
|
||||
public void SetWeatherInfoService(IWeatherInfoService weatherInfoService)
|
||||
{
|
||||
_weatherInfoService = weatherInfoService ?? DefaultWeatherInfoService;
|
||||
|
||||
@@ -75,16 +75,23 @@ public partial class WeatherClockWidget : UserControl, IDesktopComponentWidget,
|
||||
|
||||
public void SetTimeZoneService(TimeZoneService timeZoneService)
|
||||
{
|
||||
if (_timeZoneService is not null)
|
||||
{
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
}
|
||||
|
||||
ClearTimeZoneService();
|
||||
_timeZoneService = timeZoneService;
|
||||
_timeZoneService.TimeZoneChanged += OnTimeZoneChanged;
|
||||
UpdateClockVisual();
|
||||
}
|
||||
|
||||
public void ClearTimeZoneService()
|
||||
{
|
||||
if (_timeZoneService is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
_timeZoneService = null;
|
||||
}
|
||||
|
||||
public void SetWeatherInfoService(IWeatherInfoService weatherInfoService)
|
||||
{
|
||||
_weatherInfoService = weatherInfoService ?? DefaultWeatherInfoService;
|
||||
|
||||
@@ -137,15 +137,22 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, ITime
|
||||
|
||||
public void SetTimeZoneService(TimeZoneService timeZoneService)
|
||||
{
|
||||
if (_timeZoneService is not null)
|
||||
{
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
}
|
||||
|
||||
ClearTimeZoneService();
|
||||
_timeZoneService = timeZoneService;
|
||||
_timeZoneService.TimeZoneChanged += OnTimeZoneChanged;
|
||||
}
|
||||
|
||||
public void ClearTimeZoneService()
|
||||
{
|
||||
if (_timeZoneService is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
|
||||
_timeZoneService = null;
|
||||
}
|
||||
|
||||
public void SetWeatherInfoService(IWeatherInfoService weatherInfoService)
|
||||
{
|
||||
_weatherInfoService = weatherInfoService ?? DefaultWeatherInfoService;
|
||||
|
||||
@@ -400,6 +400,7 @@ public partial class MainWindow
|
||||
}
|
||||
|
||||
ComponentLibraryWindow.IsVisible = false;
|
||||
ClearComponentLibraryPreviewControls();
|
||||
|
||||
var shouldReopenSettings = reopenSettings && _reopenSettingsAfterComponentLibraryClose;
|
||||
_reopenSettingsAfterComponentLibraryClose = false;
|
||||
@@ -661,7 +662,8 @@ public partial class MainWindow
|
||||
return;
|
||||
}
|
||||
|
||||
// 娴犲海缍夐弽闂磋厬缁夊娅庣紒鍕
|
||||
ClearTimeZoneServiceBindings(_selectedDesktopComponentHost);
|
||||
|
||||
if (_desktopPageComponentGrids.TryGetValue(placement.PageIndex, out var pageGrid))
|
||||
{
|
||||
pageGrid.Children.Remove(_selectedDesktopComponentHost);
|
||||
@@ -748,7 +750,7 @@ public partial class MainWindow
|
||||
RebuildDesktopGrid();
|
||||
PersistSettings();
|
||||
|
||||
// 閺囧瓨鏌婇崝銊︹偓浣锋崲閸斺剝鐖弰鍓с仛
|
||||
// Refresh taskbar actions after page count changes.
|
||||
ApplyTaskbarActionVisibility(GetCurrentTaskbarContext());
|
||||
}
|
||||
|
||||
@@ -762,6 +764,11 @@ public partial class MainWindow
|
||||
var placementsToRemove = _desktopComponentPlacements
|
||||
.Where(p => p.PageIndex == _currentDesktopSurfaceIndex)
|
||||
.ToList();
|
||||
|
||||
if (_desktopPageComponentGrids.TryGetValue(_currentDesktopSurfaceIndex, out var pageGrid))
|
||||
{
|
||||
ClearTimeZoneServiceBindings(pageGrid.Children.OfType<Control>().ToList());
|
||||
}
|
||||
|
||||
foreach (var placement in placementsToRemove)
|
||||
{
|
||||
@@ -770,7 +777,7 @@ public partial class MainWindow
|
||||
|
||||
_desktopPageCount = Math.Clamp(_desktopPageCount - 1, MinDesktopPageCount, MaxDesktopPageCount);
|
||||
|
||||
// 鐠嬪啯鏆hぐ鎾冲妞ょ敻娼扮槐銏犵穿
|
||||
// Clamp current page index to valid range after deletion.
|
||||
_currentDesktopSurfaceIndex = Math.Clamp(_currentDesktopSurfaceIndex, 0, _desktopPageCount - 1);
|
||||
|
||||
// Update remaining page indices after deletion.
|
||||
@@ -785,7 +792,7 @@ public partial class MainWindow
|
||||
RebuildDesktopGrid();
|
||||
PersistSettings();
|
||||
|
||||
// 閺囧瓨鏌婇崝銊︹偓浣锋崲閸斺剝鐖弰鍓с仛
|
||||
// Refresh taskbar actions after page count changes.
|
||||
ApplyTaskbarActionVisibility(GetCurrentTaskbarContext());
|
||||
}
|
||||
|
||||
@@ -842,6 +849,7 @@ public partial class MainWindow
|
||||
return;
|
||||
}
|
||||
|
||||
ClearTimeZoneServiceBindings(pageGrid.Children.OfType<Control>().ToList());
|
||||
pageGrid.Children.Clear();
|
||||
|
||||
var maxColumns = pageGrid.ColumnDefinitions.Count;
|
||||
@@ -1062,6 +1070,11 @@ public partial class MainWindow
|
||||
runtimeDescriptor.Definition,
|
||||
span.WidthCells,
|
||||
span.HeightCells);
|
||||
if (runtimeDescriptor.Definition.ResizeMode == DesktopComponentResizeMode.Free)
|
||||
{
|
||||
return normalized;
|
||||
}
|
||||
|
||||
return NormalizeAspectRatioForComponent(componentId, normalized);
|
||||
}
|
||||
|
||||
@@ -1070,6 +1083,16 @@ public partial class MainWindow
|
||||
(Math.Max(1, span.WidthCells), Math.Max(1, span.HeightCells)));
|
||||
}
|
||||
|
||||
private DesktopComponentResizeMode GetComponentResizeMode(string componentId)
|
||||
{
|
||||
if (_componentRuntimeRegistry.TryGetDescriptor(componentId, out var runtimeDescriptor))
|
||||
{
|
||||
return runtimeDescriptor.Definition.ResizeMode;
|
||||
}
|
||||
|
||||
return DesktopComponentResizeMode.Proportional;
|
||||
}
|
||||
|
||||
private static (int WidthCells, int HeightCells) NormalizeAspectRatioForComponent(
|
||||
string componentId,
|
||||
(int WidthCells, int HeightCells) span)
|
||||
@@ -1196,6 +1219,30 @@ public partial class MainWindow
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void ClearTimeZoneServiceBindings(IEnumerable<Control> roots)
|
||||
{
|
||||
foreach (var root in roots)
|
||||
{
|
||||
ClearTimeZoneServiceBindings(root);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ClearTimeZoneServiceBindings(Control root)
|
||||
{
|
||||
if (root is ITimeZoneAwareComponentWidget timeZoneAwareRoot)
|
||||
{
|
||||
timeZoneAwareRoot.ClearTimeZoneService();
|
||||
}
|
||||
|
||||
foreach (var descendant in root.GetVisualDescendants())
|
||||
{
|
||||
if (descendant is ITimeZoneAwareComponentWidget timeZoneAwareChild)
|
||||
{
|
||||
timeZoneAwareChild.ClearTimeZoneService();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Border? TryGetResizeHandle(Border host)
|
||||
{
|
||||
if (host.Child is Grid hostChrome)
|
||||
@@ -1257,6 +1304,7 @@ public partial class MainWindow
|
||||
CancelDesktopComponentResize(restoreOriginalSpan: true);
|
||||
ClearDesktopComponentSelection();
|
||||
UpdateDesktopComponentHostEditState();
|
||||
ClearComponentLibraryPreviewControls();
|
||||
UpdateComponentLibraryLayout(_currentDesktopCellSize);
|
||||
}
|
||||
|
||||
@@ -1615,36 +1663,52 @@ public partial class MainWindow
|
||||
|
||||
var deltaX = pointerInViewport.X - _desktopComponentResize.StartPointerInViewport.X;
|
||||
var deltaY = pointerInViewport.Y - _desktopComponentResize.StartPointerInViewport.Y;
|
||||
var widthScale = (_desktopComponentResize.StartWidthCells + deltaX / pitch) / _desktopComponentResize.StartWidthCells;
|
||||
var heightScale = (_desktopComponentResize.StartHeightCells + deltaY / pitch) / _desktopComponentResize.StartHeightCells;
|
||||
|
||||
var proposedScale = Math.Max(widthScale, heightScale);
|
||||
var minScale = Math.Max(
|
||||
(double)_desktopComponentResize.MinWidthCells / _desktopComponentResize.StartWidthCells,
|
||||
(double)_desktopComponentResize.MinHeightCells / _desktopComponentResize.StartHeightCells);
|
||||
var maxScale = Math.Min(
|
||||
(double)_desktopComponentResize.MaxWidthCells / _desktopComponentResize.StartWidthCells,
|
||||
(double)_desktopComponentResize.MaxHeightCells / _desktopComponentResize.StartHeightCells);
|
||||
|
||||
if (double.IsNaN(proposedScale) || double.IsInfinity(proposedScale))
|
||||
int widthCells;
|
||||
int heightCells;
|
||||
if (GetComponentResizeMode(_desktopComponentResize.ComponentId) == DesktopComponentResizeMode.Free)
|
||||
{
|
||||
proposedScale = minScale;
|
||||
widthCells = Math.Clamp(
|
||||
(int)Math.Round(_desktopComponentResize.StartWidthCells + deltaX / pitch),
|
||||
_desktopComponentResize.MinWidthCells,
|
||||
_desktopComponentResize.MaxWidthCells);
|
||||
heightCells = Math.Clamp(
|
||||
(int)Math.Round(_desktopComponentResize.StartHeightCells + deltaY / pitch),
|
||||
_desktopComponentResize.MinHeightCells,
|
||||
_desktopComponentResize.MaxHeightCells);
|
||||
}
|
||||
|
||||
if (maxScale < minScale)
|
||||
else
|
||||
{
|
||||
maxScale = minScale;
|
||||
}
|
||||
var widthScale = (_desktopComponentResize.StartWidthCells + deltaX / pitch) / _desktopComponentResize.StartWidthCells;
|
||||
var heightScale = (_desktopComponentResize.StartHeightCells + deltaY / pitch) / _desktopComponentResize.StartHeightCells;
|
||||
|
||||
var scale = Math.Clamp(proposedScale, minScale, maxScale);
|
||||
var widthCells = Math.Clamp(
|
||||
(int)Math.Round(_desktopComponentResize.StartWidthCells * scale),
|
||||
_desktopComponentResize.MinWidthCells,
|
||||
_desktopComponentResize.MaxWidthCells);
|
||||
var heightCells = Math.Clamp(
|
||||
(int)Math.Round(_desktopComponentResize.StartHeightCells * scale),
|
||||
_desktopComponentResize.MinHeightCells,
|
||||
_desktopComponentResize.MaxHeightCells);
|
||||
var proposedScale = Math.Max(widthScale, heightScale);
|
||||
var minScale = Math.Max(
|
||||
(double)_desktopComponentResize.MinWidthCells / _desktopComponentResize.StartWidthCells,
|
||||
(double)_desktopComponentResize.MinHeightCells / _desktopComponentResize.StartHeightCells);
|
||||
var maxScale = Math.Min(
|
||||
(double)_desktopComponentResize.MaxWidthCells / _desktopComponentResize.StartWidthCells,
|
||||
(double)_desktopComponentResize.MaxHeightCells / _desktopComponentResize.StartHeightCells);
|
||||
|
||||
if (double.IsNaN(proposedScale) || double.IsInfinity(proposedScale))
|
||||
{
|
||||
proposedScale = minScale;
|
||||
}
|
||||
|
||||
if (maxScale < minScale)
|
||||
{
|
||||
maxScale = minScale;
|
||||
}
|
||||
|
||||
var scale = Math.Clamp(proposedScale, minScale, maxScale);
|
||||
widthCells = Math.Clamp(
|
||||
(int)Math.Round(_desktopComponentResize.StartWidthCells * scale),
|
||||
_desktopComponentResize.MinWidthCells,
|
||||
_desktopComponentResize.MaxWidthCells);
|
||||
heightCells = Math.Clamp(
|
||||
(int)Math.Round(_desktopComponentResize.StartHeightCells * scale),
|
||||
_desktopComponentResize.MinHeightCells,
|
||||
_desktopComponentResize.MaxHeightCells);
|
||||
}
|
||||
|
||||
var normalized = NormalizeComponentCellSpan(_desktopComponentResize.ComponentId, (widthCells, heightCells));
|
||||
widthCells = Math.Clamp(normalized.WidthCells, _desktopComponentResize.MinWidthCells, _desktopComponentResize.MaxWidthCells);
|
||||
@@ -2213,6 +2277,7 @@ public partial class MainWindow
|
||||
_componentLibraryActiveComponents = category.Components;
|
||||
var componentCount = _componentLibraryActiveComponents.Count;
|
||||
|
||||
ClearTimeZoneServiceBindings(ComponentLibraryComponentPagesContainer.Children.OfType<Control>().ToList());
|
||||
ComponentLibraryComponentPagesContainer.Children.Clear();
|
||||
ComponentLibraryComponentPagesContainer.RowDefinitions.Clear();
|
||||
ComponentLibraryComponentPagesContainer.ColumnDefinitions.Clear();
|
||||
@@ -2374,6 +2439,19 @@ public partial class MainWindow
|
||||
UpdateComponentLibraryComponentNavigationButtons();
|
||||
}
|
||||
|
||||
private void ClearComponentLibraryPreviewControls()
|
||||
{
|
||||
if (ComponentLibraryComponentPagesContainer is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ClearTimeZoneServiceBindings(ComponentLibraryComponentPagesContainer.Children.OfType<Control>().ToList());
|
||||
ComponentLibraryComponentPagesContainer.Children.Clear();
|
||||
ComponentLibraryComponentPagesContainer.RowDefinitions.Clear();
|
||||
ComponentLibraryComponentPagesContainer.ColumnDefinitions.Clear();
|
||||
}
|
||||
|
||||
private string GetLocalizedComponentDisplayName(DesktopComponentRuntimeDescriptor descriptor)
|
||||
{
|
||||
return L(descriptor.DisplayNameLocalizationKey, descriptor.Definition.DisplayName);
|
||||
|
||||
@@ -127,6 +127,7 @@ public partial class MainWindow
|
||||
DesktopPagesContainer.RowDefinitions.Clear();
|
||||
DesktopPagesContainer.RowDefinitions.Add(new RowDefinition(new GridLength(pageHeight, GridUnitType.Pixel)));
|
||||
DesktopPagesContainer.ColumnDefinitions.Clear();
|
||||
ClearTimeZoneServiceBindings(DesktopPagesContainer.Children.OfType<Control>().ToList());
|
||||
DesktopPagesContainer.Children.Clear();
|
||||
DesktopPagesContainer.Width = pageWidth * _desktopPageCount;
|
||||
DesktopPagesContainer.Height = pageHeight;
|
||||
|
||||
@@ -86,20 +86,22 @@ public partial class MainWindow
|
||||
WallpaperPreviewBackButtonTextBlock.Text = L("button.back_to_windows", "Back to Windows");
|
||||
ToolTip.SetTip(BackToWindowsButton, L("tooltip.back_to_windows", "Back to Windows"));
|
||||
|
||||
OpenComponentLibraryTextBlock.Text = L("button.component_library", "编辑桌面");
|
||||
WallpaperPreviewComponentLibraryTextBlock.Text = L("button.component_library", "编辑桌面");
|
||||
GridPreviewComponentLibraryTextBlock.Text = L("button.component_library", "编辑桌面");
|
||||
ToolTip.SetTip(OpenComponentLibraryButton, L("tooltip.component_library", "编辑桌面"));
|
||||
ComponentLibraryTitleTextBlock.Text = L("component_library.title", "小组件");
|
||||
OpenComponentLibraryTextBlock.Text = L("button.component_library", "Edit Desktop");
|
||||
WallpaperPreviewComponentLibraryTextBlock.Text = L("button.component_library", "Edit Desktop");
|
||||
GridPreviewComponentLibraryTextBlock.Text = L("button.component_library", "Edit Desktop");
|
||||
ToolTip.SetTip(OpenComponentLibraryButton, L("tooltip.component_library", "Edit Desktop"));
|
||||
ComponentLibraryTitleTextBlock.Text = L("component_library.title", "Widgets");
|
||||
ToolTip.SetTip(CloseComponentLibraryButton, L("common.close", "Close"));
|
||||
ComponentLibraryEmptyTextBlock.Text = L(
|
||||
"component_library.empty",
|
||||
"Swipe to pick a category, tap to open, then drag a widget onto the desktop.");
|
||||
|
||||
LauncherTitleTextBlock.Text = L("launcher.title", "应用启动台");
|
||||
LauncherSubtitleTextBlock.Text = L("launcher.subtitle", "按 Windows 开始菜单结构显示所有应用与文件夹");
|
||||
ToolTip.SetTip(LauncherFolderBackButton, L("common.back", "返回"));
|
||||
ToolTip.SetTip(LauncherFolderCloseButton, L("common.close", "关闭"));
|
||||
LauncherTitleTextBlock.Text = L("launcher.title", "App Launcher");
|
||||
LauncherSubtitleTextBlock.Text = L(
|
||||
"launcher.subtitle",
|
||||
"Displays all apps and folders based on the Windows Start menu structure.");
|
||||
ToolTip.SetTip(LauncherFolderBackButton, L("common.back", "Back"));
|
||||
ToolTip.SetTip(LauncherFolderCloseButton, L("common.close", "Close"));
|
||||
|
||||
SettingsNavHeaderTextBlock.Text = L("settings.nav_header", "Settings");
|
||||
SettingsNavWallpaperTextBlock.Text = L("settings.nav.wallpaper", "Wallpaper");
|
||||
@@ -109,11 +111,13 @@ public partial class MainWindow
|
||||
SettingsNavWeatherTextBlock.Text = L("settings.nav.weather", "Weather");
|
||||
SettingsNavRegionTextBlock.Text = L("settings.nav.region", "Region");
|
||||
|
||||
WallpaperPanelTitleTextBlock.Text = L("settings.wallpaper.title", "个性化我们的背景");
|
||||
WallpaperPlacementSettingsExpander.Header = L("settings.wallpaper.placement_label", "选择契合度");
|
||||
WallpaperPlacementSettingsExpander.Description = L("settings.wallpaper.placement_desc", "调整图像在桌面上的填充方式。");
|
||||
PickWallpaperButton.Content = L("settings.wallpaper.pick_button", "浏览照片");
|
||||
ClearWallpaperButton.Content = L("settings.wallpaper.clear_button", "重置");
|
||||
WallpaperPanelTitleTextBlock.Text = L("settings.wallpaper.title", "Personalize your wallpaper");
|
||||
WallpaperPlacementSettingsExpander.Header = L("settings.wallpaper.placement_label", "Placement");
|
||||
WallpaperPlacementSettingsExpander.Description = L(
|
||||
"settings.wallpaper.placement_desc",
|
||||
"Adjust how the image fits on the desktop.");
|
||||
PickWallpaperButton.Content = L("settings.wallpaper.pick_button", "Browse");
|
||||
ClearWallpaperButton.Content = L("settings.wallpaper.clear_button", "Reset");
|
||||
|
||||
GridPanelTitleTextBlock.Text = L("settings.grid.title", "Grid Layout");
|
||||
GridSpacingPresetLabelTextBlock.Text = L("settings.grid.spacing_label", "Grid Spacing");
|
||||
@@ -265,7 +269,7 @@ public partial class MainWindow
|
||||
|
||||
private string GetLocalizedTimeZoneDisplayName(TimeZoneInfo timeZone)
|
||||
{
|
||||
var offset = timeZone.BaseUtcOffset;
|
||||
var offset = timeZone.GetUtcOffset(DateTime.UtcNow);
|
||||
var sign = offset >= TimeSpan.Zero ? "+" : "-";
|
||||
var hours = Math.Abs(offset.Hours);
|
||||
var minutes = Math.Abs(offset.Minutes);
|
||||
|
||||
@@ -476,7 +476,7 @@
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||
Margin="0,0,0,24"
|
||||
Text="涓€у寲鎮ㄧ殑鑳屾櫙" />
|
||||
Text="Personalize Wallpaper" />
|
||||
|
||||
<!-- Left Column: Monitor Preview -->
|
||||
<Border x:Name="WallpaperPreviewHost"
|
||||
@@ -563,11 +563,11 @@
|
||||
FontWeight="Medium"
|
||||
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Text="鏈€夋嫨鏂囦欢" />
|
||||
Text="No file selected" />
|
||||
<TextBlock x:Name="WallpaperStatusTextBlock"
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource AdaptiveTextMutedBrush}"
|
||||
Text="灏辩华" />
|
||||
Text="Ready" />
|
||||
</StackPanel>
|
||||
|
||||
<Separator Background="{DynamicResource SurfaceStrokeColorDefaultBrush}" Height="1" Margin="0,8" />
|
||||
@@ -581,13 +581,13 @@
|
||||
HorizontalAlignment="Stretch"
|
||||
Padding="0,10"
|
||||
Click="OnPickWallpaperClick"
|
||||
Content="娴忚鐓х墖" />
|
||||
Content="Browse" />
|
||||
<Button x:Name="ClearWallpaperButton"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
Padding="0,10"
|
||||
Click="OnClearWallpaperClick"
|
||||
Content="閲嶇疆" />
|
||||
Content="Reset" />
|
||||
</Grid>
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
@@ -695,7 +695,7 @@
|
||||
<StackPanel Grid.Row="1" Grid.Column="1"
|
||||
Margin="16,0,0,0"
|
||||
Spacing="16">
|
||||
<TextBlock Text="绔栨帓鏍兼暟" FontSize="16" FontWeight="SemiBold" Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||
<TextBlock Text="Rows" FontSize="16" FontWeight="SemiBold" Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||
|
||||
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="12">
|
||||
<Slider x:Name="GridSizeSlider"
|
||||
@@ -754,7 +754,7 @@
|
||||
|
||||
<TextBlock x:Name="GridEdgeInsetComputedPxTextBlock"
|
||||
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"
|
||||
Text="鈮?0 px" />
|
||||
Text=">= 0 px" />
|
||||
|
||||
<Button x:Name="ApplyGridButton"
|
||||
HorizontalAlignment="Stretch"
|
||||
@@ -1005,15 +1005,15 @@
|
||||
Unchecked="OnStatusBarClockUnchecked" />
|
||||
</ui:SettingsExpander.Footer>
|
||||
<StackPanel Margin="0,8,0,0" Spacing="12">
|
||||
<TextBlock Text="鏄剧ず鏍煎紡" FontSize="14" FontWeight="SemiBold" Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||
<TextBlock Text="Display Format" FontSize="14" FontWeight="SemiBold" Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<RadioButton x:Name="ClockFormatHMSSRadio"
|
||||
Content="鏃跺垎绉?(HH:mm:ss)"
|
||||
Content="Time with seconds (HH:mm:ss)"
|
||||
GroupName="ClockFormat"
|
||||
Checked="OnClockFormatChanged"
|
||||
Tag="Hms" />
|
||||
<RadioButton x:Name="ClockFormatHMRadio"
|
||||
Content="鏃跺垎 (HH:mm)"
|
||||
Content="Time (HH:mm)"
|
||||
GroupName="ClockFormat"
|
||||
Checked="OnClockFormatChanged"
|
||||
Tag="Hm" />
|
||||
@@ -1070,7 +1070,7 @@
|
||||
|
||||
<TextBlock x:Name="StatusBarSpacingComputedPxTextBlock"
|
||||
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"
|
||||
Text="鈮?0 px" />
|
||||
Text=">= 0 px" />
|
||||
</StackPanel>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
@@ -1274,19 +1274,19 @@
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel x:Name="AboutSettingsPanel" IsVisible="False" Spacing="20">
|
||||
<TextBlock x:Name="AboutPanelTitleTextBlock" FontSize="24" FontWeight="SemiBold" Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" Text="鍏充簬" />
|
||||
<StackPanel x:Name="AboutSettingsPanel" IsVisible="False" Spacing="20">
|
||||
<TextBlock x:Name="AboutPanelTitleTextBlock" FontSize="24" FontWeight="SemiBold" Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" Text="About" />
|
||||
<Border Background="{DynamicResource AdaptiveSurfaceRaisedBrush}" CornerRadius="{DynamicResource DesignCornerRadiusMd}" Padding="20">
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock Text="LanMontainDesktop" FontSize="20" FontWeight="SemiBold" Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||
<TextBlock Text="Modern desktop shell experience." 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="鐗堟湰浠e彿: Administrate" FontSize="13" FontWeight="SemiBold" Foreground="{DynamicResource AdaptiveAccentBrush}" />
|
||||
<TextBlock x:Name="FontInfoTextBlock" Text="字体: MiSans" FontSize="12" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
|
||||
<TextBlock x:Name="VersionTextBlock" Text="Version: 1.0.0" FontSize="13" Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||
<TextBlock x:Name="CodeNameTextBlock" Text="Code Name: Administrate" FontSize="13" FontWeight="SemiBold" Foreground="{DynamicResource AdaptiveAccentBrush}" />
|
||||
<TextBlock x:Name="FontInfoTextBlock" Text="Font: MiSans" FontSize="12" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
@@ -1314,7 +1314,7 @@
|
||||
CornerRadius="36,36,0,0"
|
||||
Padding="16,12">
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<TextBlock Text="缁勪欢璁剧疆"
|
||||
<TextBlock Text="Component Settings"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="White"
|
||||
|
||||
@@ -795,7 +795,7 @@ public partial class MainWindow : Window
|
||||
var spacingPx = Math.Max(0, cellSize) * (percent / 100d);
|
||||
StatusBarSpacingComputedPxTextBlock.Text = Lf(
|
||||
"settings.status_bar.spacing_custom_px_format",
|
||||
"鈮?{0:F1}px",
|
||||
">= {0:F1}px",
|
||||
spacingPx);
|
||||
}
|
||||
|
||||
@@ -863,13 +863,11 @@ public partial class MainWindow : Window
|
||||
var shortSidePx = Math.Max(1, Math.Min(hostWidth, hostHeight));
|
||||
var baseCell = shortSidePx / cells;
|
||||
|
||||
// --- 姣斾緥鍖栫暀鐧?(Proportional Inset) ---
|
||||
// 鍏佽鐢ㄦ埛鐧惧垎姣旇皟鑺傦紝浣嗚瀹氭洿鍚堢悊鐨勫熀鍑嗗拰闄愬埗
|
||||
// Proportional inset based on user percentage selection.
|
||||
var clampedPercent = Math.Clamp(insetPercent, MinEdgeInsetPercent, MaxEdgeInsetPercent);
|
||||
var insetRatio = clampedPercent / 100d;
|
||||
|
||||
// 纭繚鏈€灏忕暀鐧借兘瀹圭撼涓€瀹氱殑闃村奖鎵╁睍
|
||||
// 鍏佽 0 杈硅窛锛屾渶澶т笂闄愮淮鎸?80px
|
||||
// Keep inset within a practical visual range.
|
||||
return Math.Clamp(baseCell * insetRatio, 0, 80);
|
||||
}
|
||||
|
||||
@@ -1282,7 +1280,7 @@ public partial class MainWindow : Window
|
||||
};
|
||||
TimeZoneComboBox.Items.Add(item);
|
||||
|
||||
// 閫変腑褰撳墠鏃跺尯
|
||||
// Select current time zone.
|
||||
if (tz.Id == _timeZoneService.CurrentTimeZone.Id)
|
||||
{
|
||||
TimeZoneComboBox.SelectedItem = item;
|
||||
|
||||
Reference in New Issue
Block a user