DragAndDrop core
This commit is contained in:
19
Lattice.Themes.Core/Lattice.Themes.Core.csproj
Normal file
19
Lattice.Themes.Core/Lattice.Themes.Core.csproj
Normal file
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0-windows10.0.19041.0;net9.0-windows10.0.19041.0;net10.0-windows10.0.19041.0</TargetFrameworks>
|
||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>Lattice.Themes.Core</RootNamespace>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<WinUISDKReferences>false</WinUISDKReferences>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7463" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
231
Lattice.Themes.Core/LatticeTokens.cs
Normal file
231
Lattice.Themes.Core/LatticeTokens.cs
Normal file
@@ -0,0 +1,231 @@
|
||||
namespace Lattice.Themes.Core.Tokens;
|
||||
|
||||
/// <summary>
|
||||
/// Статические ключи для ресурсов Lattice Framework.
|
||||
/// Используются в XAML через {DynamicResource} или {ThemeResource}.
|
||||
/// </summary>
|
||||
public static class LatticeTokens
|
||||
{
|
||||
// ============ Цвета (Colors) ============
|
||||
|
||||
// Основные цвета
|
||||
public const string ColorPrimary = "Lattice.Color.Primary";
|
||||
public const string ColorSecondary = "Lattice.Color.Secondary";
|
||||
public const string ColorTertiary = "Lattice.Color.Tertiary";
|
||||
|
||||
// Акцентные цвета
|
||||
public const string ColorAccent = "Lattice.Color.Accent";
|
||||
public const string ColorAccentLight = "Lattice.Color.Accent.Light";
|
||||
public const string ColorAccentDark = "Lattice.Color.Accent.Dark";
|
||||
public const string ColorAccentAction = "Lattice.Color.Accent.Action";
|
||||
|
||||
// Фоновые цвета
|
||||
public const string ColorBackgroundPrimary = "Lattice.Color.Background.Primary";
|
||||
public const string ColorBackgroundSecondary = "Lattice.Color.Background.Secondary";
|
||||
public const string ColorBackgroundTertiary = "Lattice.Color.Background.Tertiary";
|
||||
public const string ColorBackgroundQuaternary = "Lattice.Color.Background.Quaternary";
|
||||
|
||||
// Текстовые цвета
|
||||
public const string ColorTextPrimary = "Lattice.Color.Text.Primary";
|
||||
public const string ColorTextSecondary = "Lattice.Color.Text.Secondary";
|
||||
public const string ColorTextDisabled = "Lattice.Color.Text.Disabled";
|
||||
public const string ColorTextOnAccent = "Lattice.Color.Text.OnAccent";
|
||||
|
||||
// Граничные цвета
|
||||
public const string ColorBorderPrimary = "Lattice.Color.Border.Primary";
|
||||
public const string ColorBorderSecondary = "Lattice.Color.Border.Secondary";
|
||||
public const string ColorBorderAccent = "Lattice.Color.Border.Accent";
|
||||
|
||||
// Состояния
|
||||
public const string ColorSuccess = "Lattice.Color.Success";
|
||||
public const string ColorWarning = "Lattice.Color.Warning";
|
||||
public const string ColorError = "Lattice.Color.Error";
|
||||
public const string ColorInfo = "Lattice.Color.Info";
|
||||
|
||||
// ============ Кисти (Brushes) ============
|
||||
|
||||
// Основные кисти
|
||||
public const string BrushPrimary = "Lattice.Brush.Primary";
|
||||
public const string BrushSecondary = "Lattice.Brush.Secondary";
|
||||
public const string BrushTertiary = "Lattice.Brush.Tertiary";
|
||||
|
||||
// Акцентные кисти
|
||||
public const string BrushAccent = "Lattice.Brush.Accent";
|
||||
public const string BrushAccentLight = "Lattice.Brush.Accent.Light";
|
||||
public const string BrushAccentDark = "Lattice.Brush.Accent.Dark";
|
||||
public const string BrushAccentAction = "Lattice.Brush.Accent.Action";
|
||||
|
||||
// Фоновые кисти
|
||||
public const string BrushBackgroundPrimary = "Lattice.Brush.Background.Primary";
|
||||
public const string BrushBackgroundSecondary = "Lattice.Brush.Background.Secondary";
|
||||
public const string BrushBackgroundTertiary = "Lattice.Brush.Background.Tertiary";
|
||||
public const string BrushBackgroundQuaternary = "Lattice.Brush.Background.Quaternary";
|
||||
|
||||
// Текстовые кисти
|
||||
public const string BrushTextPrimary = "Lattice.Brush.Text.Primary";
|
||||
public const string BrushTextSecondary = "Lattice.Brush.Text.Secondary";
|
||||
public const string BrushTextDisabled = "Lattice.Brush.Text.Disabled";
|
||||
public const string BrushTextOnAccent = "Lattice.Brush.Text.OnAccent";
|
||||
|
||||
// Граничные кисти
|
||||
public const string BrushBorderPrimary = "Lattice.Brush.Border.Primary";
|
||||
public const string BrushBorderSecondary = "Lattice.Brush.Border.Secondary";
|
||||
public const string BrushBorderAccent = "Lattice.Brush.Border.Accent";
|
||||
|
||||
// Кисти состояний
|
||||
public const string BrushSuccess = "Lattice.Brush.Success";
|
||||
public const string BrushWarning = "Lattice.Brush.Warning";
|
||||
public const string BrushError = "Lattice.Brush.Error";
|
||||
public const string BrushInfo = "Lattice.Brush.Info";
|
||||
|
||||
// Кисти для перетаскивания
|
||||
public const string BrushDragOverlay = "Lattice.Brush.Drag.Overlay";
|
||||
public const string BrushDropPreview = "Lattice.Brush.Drop.Preview";
|
||||
public const string BrushDropValid = "Lattice.Brush.Drop.Valid";
|
||||
public const string BrushDropInvalid = "Lattice.Brush.Drop.Invalid";
|
||||
|
||||
// Кисти для панелей и разделителей
|
||||
public const string BrushPanelBorder = "Lattice.Brush.Panel.Border";
|
||||
public const string BrushSplitterNormal = "Lattice.Brush.Splitter.Normal";
|
||||
public const string BrushSplitterHover = "Lattice.Brush.Splitter.Hover";
|
||||
|
||||
// ============ Геометрия (Geometry) ============
|
||||
|
||||
public const string SizeSplitterWidth = "Lattice.Size.Splitter.Width";
|
||||
|
||||
// Радиусы скругления
|
||||
public const string CornerRadiusNone = "Lattice.CornerRadius.None";
|
||||
public const string CornerRadiusSmall = "Lattice.CornerRadius.Small";
|
||||
public const string CornerRadiusMedium = "Lattice.CornerRadius.Medium";
|
||||
public const string CornerRadiusLarge = "Lattice.CornerRadius.Large";
|
||||
public const string CornerRadiusXLarge = "Lattice.CornerRadius.XLarge";
|
||||
public const string CornerRadiusCircle = "Lattice.CornerRadius.Circle";
|
||||
public const string CornerRadiusPanel = "Lattice.CornerRadius.Panel";
|
||||
|
||||
// Отступы
|
||||
public const string SpacingNone = "Lattice.Spacing.None";
|
||||
public const string SpacingXSmall = "Lattice.Spacing.XSmall";
|
||||
public const string SpacingSmall = "Lattice.Spacing.Small";
|
||||
public const string SpacingMedium = "Lattice.Spacing.Medium";
|
||||
public const string SpacingLarge = "Lattice.Spacing.Large";
|
||||
public const string SpacingXLarge = "Lattice.Spacing.XLarge";
|
||||
public const string SpacingXXLarge = "Lattice.Spacing.XXLarge";
|
||||
public const string SpacingPanel = "Lattice.Spacing.Panel";
|
||||
|
||||
// Толщины границ
|
||||
public const string BorderThicknessNone = "Lattice.BorderThickness.None";
|
||||
public const string BorderThicknessThin = "Lattice.BorderThickness.Thin";
|
||||
public const string BorderThicknessMedium = "Lattice.BorderThickness.Medium";
|
||||
public const string BorderThicknessThick = "Lattice.BorderThickness.Thick";
|
||||
public const string BorderThicknessPanel = "Lattice.BorderThickness.Panel";
|
||||
|
||||
// Размеры теней
|
||||
public const string ShadowDepthNone = "Lattice.Shadow.Depth.None";
|
||||
public const string ShadowDepthSmall = "Lattice.Shadow.Depth.Small";
|
||||
public const string ShadowDepthMedium = "Lattice.Shadow.Depth.Medium";
|
||||
public const string ShadowDepthLarge = "Lattice.Shadow.Depth.Large";
|
||||
|
||||
// ============ Текстовые стили (Typography) ============
|
||||
|
||||
// Размеры шрифтов
|
||||
public const string FontSizeCaption = "Lattice.FontSize.Caption";
|
||||
public const string FontSizeBody = "Lattice.FontSize.Body";
|
||||
public const string FontSizeBodyStrong = "Lattice.FontSize.BodyStrong";
|
||||
public const string FontSizeSubtitle = "Lattice.FontSize.Subtitle";
|
||||
public const string FontSizeTitle = "Lattice.FontSize.Title";
|
||||
public const string FontSizeTitleLarge = "Lattice.FontSize.TitleLarge";
|
||||
public const string FontSizeDisplay = "Lattice.FontSize.Display";
|
||||
|
||||
// Высота строк
|
||||
public const string LineHeightTight = "Lattice.LineHeight.Tight";
|
||||
public const string LineHeightNormal = "Lattice.LineHeight.Normal";
|
||||
public const string LineHeightRelaxed = "Lattice.LineHeight.Relaxed";
|
||||
|
||||
// Веса шрифтов
|
||||
public const string FontWeightLight = "Lattice.FontWeight.Light";
|
||||
public const string FontWeightNormal = "Lattice.FontWeight.Normal";
|
||||
public const string FontWeightMedium = "Lattice.FontWeight.Medium";
|
||||
public const string FontWeightSemibold = "Lattice.FontWeight.Semibold";
|
||||
public const string FontWeightBold = "Lattice.FontWeight.Bold";
|
||||
|
||||
// ============ Анимации (Animations) ============
|
||||
|
||||
// Длительности
|
||||
public const string DurationInstant = "Lattice.Duration.Instant";
|
||||
public const string DurationFast = "Lattice.Duration.Fast";
|
||||
public const string DurationNormal = "Lattice.Duration.Normal";
|
||||
public const string DurationSlow = "Lattice.Duration.Slow";
|
||||
|
||||
// Кривые анимаций
|
||||
public const string EasingLinear = "Lattice.Easing.Linear";
|
||||
public const string EasingStandard = "Lattice.Easing.Standard";
|
||||
public const string EasingStandardAccelerate = "Lattice.Easing.Standard.Accelerate";
|
||||
public const string EasingStandardDecelerate = "Lattice.Easing.Standard.Decelerate";
|
||||
public const string EasingEmphasized = "Lattice.Easing.Emphasized";
|
||||
|
||||
// ============ Разное (Miscellaneous) ============
|
||||
|
||||
// Прозрачности
|
||||
public const string OpacityDisabled = "Lattice.Opacity.Disabled";
|
||||
public const string OpacityOverlay = "Lattice.Opacity.Overlay";
|
||||
public const string OpacityDrag = "Lattice.Opacity.Drag";
|
||||
public const string OpacityDropPreview = "Lattice.Opacity.DropPreview";
|
||||
|
||||
// Z-индексы
|
||||
public const string ZIndexDefault = "Lattice.ZIndex.Default";
|
||||
public const string ZIndexOverlay = "Lattice.ZIndex.Overlay";
|
||||
public const string ZIndexDialog = "Lattice.ZIndex.Dialog";
|
||||
public const string ZIndexTooltip = "Lattice.ZIndex.Tooltip";
|
||||
public const string ZIndexDrag = "Lattice.ZIndex.Drag";
|
||||
public const string ZIndexDropPreview = "Lattice.ZIndex.DropPreview";
|
||||
|
||||
// Размеры иконок
|
||||
public const string IconSizeSmall = "Lattice.IconSize.Small";
|
||||
public const string IconSizeMedium = "Lattice.IconSize.Medium";
|
||||
public const string IconSizeLarge = "Lattice.IconSize.Large";
|
||||
|
||||
// ============ Вспомогательные методы ============
|
||||
|
||||
/// <summary>
|
||||
/// Получает все токены как коллекцию ключ-значение.
|
||||
/// </summary>
|
||||
public static IReadOnlyDictionary<string, string> GetAllTokens()
|
||||
{
|
||||
var fields = typeof(LatticeTokens).GetFields(
|
||||
System.Reflection.BindingFlags.Public |
|
||||
System.Reflection.BindingFlags.Static);
|
||||
|
||||
var tokens = new Dictionary<string, string>();
|
||||
|
||||
foreach (var field in fields)
|
||||
{
|
||||
if (field.FieldType == typeof(string) && field.IsLiteral)
|
||||
{
|
||||
tokens[field.Name] = (string)field.GetValue(null)!;
|
||||
}
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет, существует ли токен с указанным ключом.
|
||||
/// </summary>
|
||||
public static bool ContainsToken(string key)
|
||||
{
|
||||
return GetAllTokens().ContainsKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает токен по его имени (не по значению).
|
||||
/// </summary>
|
||||
public static string? GetTokenByName(string name)
|
||||
{
|
||||
var field = typeof(LatticeTokens).GetField(
|
||||
name,
|
||||
System.Reflection.BindingFlags.Public |
|
||||
System.Reflection.BindingFlags.Static);
|
||||
|
||||
return field?.GetValue(null) as string;
|
||||
}
|
||||
}
|
||||
13
Lattice.Themes.Core/ThemeChangedEventArgs.cs
Normal file
13
Lattice.Themes.Core/ThemeChangedEventArgs.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace Lattice.Themes;
|
||||
|
||||
public sealed class ThemeChangedEventArgs : EventArgs
|
||||
{
|
||||
public ThemePack OldTheme { get; }
|
||||
public ThemePack NewTheme { get; }
|
||||
|
||||
public ThemeChangedEventArgs(ThemePack oldTheme, ThemePack newTheme)
|
||||
{
|
||||
OldTheme = oldTheme;
|
||||
NewTheme = newTheme;
|
||||
}
|
||||
}
|
||||
7
Lattice.Themes.Core/ThemeDictionary.cs
Normal file
7
Lattice.Themes.Core/ThemeDictionary.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Lattice.Themes;
|
||||
|
||||
public sealed class ThemeDictionary : ResourceDictionary
|
||||
{
|
||||
}
|
||||
293
Lattice.Themes.Core/ThemeManager.cs
Normal file
293
Lattice.Themes.Core/ThemeManager.cs
Normal file
@@ -0,0 +1,293 @@
|
||||
using Lattice.Themes.Core.Tokens;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
|
||||
namespace Lattice.Themes;
|
||||
|
||||
/// <summary>
|
||||
/// Менеджер тем для Lattice Framework.
|
||||
/// </summary>
|
||||
public sealed class ThemeManager
|
||||
{
|
||||
public static ThemeManager Current { get; } = new();
|
||||
|
||||
private ThemePack? _currentTheme;
|
||||
private readonly Dictionary<string, ThemePack> _registeredThemes = new();
|
||||
|
||||
public ThemePack? CurrentTheme => _currentTheme;
|
||||
|
||||
public event EventHandler<ThemeChangedEventArgs>? ThemeChanged;
|
||||
|
||||
private ThemeManager() { }
|
||||
|
||||
/// <summary>
|
||||
/// Регистрирует тему в менеджере.
|
||||
/// </summary>
|
||||
public void RegisterTheme(ThemePack theme)
|
||||
{
|
||||
if (theme == null)
|
||||
throw new ArgumentNullException(nameof(theme));
|
||||
|
||||
_registeredThemes[theme.Name] = theme;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает зарегистрированную тему по имени.
|
||||
/// </summary>
|
||||
public ThemePack? GetTheme(string name)
|
||||
{
|
||||
_registeredThemes.TryGetValue(name, out var theme);
|
||||
return theme;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает список всех зарегистрированных тем.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<ThemePack> GetRegisteredThemes()
|
||||
{
|
||||
return _registeredThemes.Values.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение информации о теме.
|
||||
/// </summary>
|
||||
/// <param name="themeName"></param>
|
||||
/// <returns></returns>
|
||||
public ThemeInfo GetThemeInfo(string themeName)
|
||||
{
|
||||
if (!_registeredThemes.TryGetValue(themeName, out var theme))
|
||||
return null;
|
||||
|
||||
return new ThemeInfo
|
||||
{
|
||||
Name = theme.Name,
|
||||
Description = theme.Description,
|
||||
Version = theme.Version,
|
||||
IsDark = theme.IsDark,
|
||||
TokenCount = CountTokensInTheme(theme)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Применяет тему по имени.
|
||||
/// </summary>
|
||||
public void ApplyTheme(string themeName)
|
||||
{
|
||||
if (!_registeredThemes.TryGetValue(themeName, out var theme))
|
||||
{
|
||||
throw new ArgumentException($"Theme '{themeName}' is not registered.", nameof(themeName));
|
||||
}
|
||||
|
||||
ApplyTheme(theme);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Применяет указанную тему.
|
||||
/// </summary>
|
||||
public void ApplyTheme(ThemePack theme)
|
||||
{
|
||||
if (theme == null)
|
||||
throw new ArgumentNullException(nameof(theme));
|
||||
|
||||
if (_currentTheme == theme)
|
||||
return;
|
||||
|
||||
var old = _currentTheme;
|
||||
_currentTheme = theme;
|
||||
|
||||
try
|
||||
{
|
||||
ReplaceApplicationResources(theme);
|
||||
ThemeChanged?.Invoke(this, new ThemeChangedEventArgs(old!, theme));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// В случае ошибки возвращаемся к старой теме
|
||||
_currentTheme = old;
|
||||
if (old != null)
|
||||
{
|
||||
ReplaceApplicationResources(old);
|
||||
}
|
||||
throw new InvalidOperationException($"Failed to apply theme '{theme.Name}'.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Загружает ресурсы темы в указанный словарь ресурсов.
|
||||
/// </summary>
|
||||
public void LoadThemeIntoDictionary(ResourceDictionary targetDictionary, ThemePack theme)
|
||||
{
|
||||
if (targetDictionary == null)
|
||||
throw new ArgumentNullException(nameof(targetDictionary));
|
||||
|
||||
if (theme == null)
|
||||
throw new ArgumentNullException(nameof(theme));
|
||||
|
||||
// Очищаем старые словари Lattice
|
||||
for (int i = targetDictionary.MergedDictionaries.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (targetDictionary.MergedDictionaries[i] is ThemeDictionary)
|
||||
targetDictionary.MergedDictionaries.RemoveAt(i);
|
||||
}
|
||||
|
||||
// Добавляем новые словари темы
|
||||
foreach (var uri in theme.GetResourceUris())
|
||||
{
|
||||
try
|
||||
{
|
||||
var themeDict = new ThemeDictionary { Source = uri };
|
||||
targetDictionary.MergedDictionaries.Add(themeDict);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to load theme resource from '{uri}'.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int CountTokensInTheme(ThemePack theme)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dict = new ResourceDictionary();
|
||||
LoadThemeIntoDictionary(dict, theme);
|
||||
return dict.Count;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReplaceApplicationResources(ThemePack theme)
|
||||
{
|
||||
var app = Application.Current;
|
||||
if (app == null)
|
||||
return;
|
||||
|
||||
var root = app.Resources;
|
||||
LoadThemeIntoDictionary(root, theme);
|
||||
ForceUpdateUI();
|
||||
}
|
||||
|
||||
private void ForceUpdateUI()
|
||||
{
|
||||
foreach (var window in WindowTracker.Windows)
|
||||
{
|
||||
if (window.Content is FrameworkElement root)
|
||||
RefreshElement(root);
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshElement(FrameworkElement element)
|
||||
{
|
||||
var stack = new Stack<FrameworkElement>();
|
||||
stack.Push(element);
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var current = stack.Pop();
|
||||
|
||||
// Пересоздаём Template только у Control
|
||||
if (current is Control control)
|
||||
{
|
||||
var template = control.Template;
|
||||
control.Template = null;
|
||||
control.Template = template;
|
||||
}
|
||||
else if (current is ContentPresenter contentPresenter)
|
||||
{
|
||||
// Обновляем ContentPresenter
|
||||
var content = contentPresenter.Content;
|
||||
contentPresenter.Content = null;
|
||||
contentPresenter.Content = content;
|
||||
}
|
||||
|
||||
// Добавляем детей в стек
|
||||
int count = VisualTreeHelper.GetChildrenCount(current);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (VisualTreeHelper.GetChild(current, i) is FrameworkElement child)
|
||||
stack.Push(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет, что все необходимые токены определены в текущей теме.
|
||||
/// </summary>
|
||||
public bool ValidateThemeTokens()
|
||||
{
|
||||
if (_currentTheme == null)
|
||||
return false;
|
||||
|
||||
var app = Application.Current;
|
||||
if (app == null)
|
||||
return false;
|
||||
|
||||
var requiredTokens = LatticeTokens.GetAllTokens().Values;
|
||||
var missingTokens = new List<string>();
|
||||
|
||||
foreach (var token in requiredTokens)
|
||||
{
|
||||
if (!app.Resources.ContainsKey(token))
|
||||
{
|
||||
missingTokens.Add(token);
|
||||
}
|
||||
}
|
||||
|
||||
if (missingTokens.Any())
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Missing theme tokens: {string.Join(", ", missingTokens)}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает значение токена из текущей темы.
|
||||
/// </summary>
|
||||
public object? GetTokenValue(string tokenKey)
|
||||
{
|
||||
var app = Application.Current;
|
||||
if (app == null)
|
||||
return null;
|
||||
|
||||
if (app.Resources.TryGetValue(tokenKey, out var value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает значение токена с приведением к указанному типу.
|
||||
/// </summary>
|
||||
public T? GetTokenValue<T>(string tokenKey)
|
||||
{
|
||||
object? value = GetTokenValue(tokenKey);
|
||||
|
||||
if (value is T typedValue)
|
||||
return typedValue;
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Информация о теме.
|
||||
/// </summary>
|
||||
public class ThemeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Название темы.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Version { get; set; }
|
||||
public bool IsDark { get; set; }
|
||||
public int TokenCount { get; set; }
|
||||
}
|
||||
111
Lattice.Themes.Core/ThemePack.cs
Normal file
111
Lattice.Themes.Core/ThemePack.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Lattice.Themes;
|
||||
|
||||
/// <summary>
|
||||
/// Базовый класс для пакетов тем.
|
||||
/// </summary>
|
||||
public abstract class ThemePack : INotifyPropertyChanged
|
||||
{
|
||||
private string _name;
|
||||
private string _description = string.Empty;
|
||||
private string _version = "1.0.0";
|
||||
private bool _isDark;
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
protected ThemePack(string name)
|
||||
{
|
||||
_name = name ?? throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Название темы.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set
|
||||
{
|
||||
if (_name != value)
|
||||
{
|
||||
_name = value;
|
||||
OnPropertyChanged(nameof(Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Описание темы.
|
||||
/// </summary>
|
||||
public string Description
|
||||
{
|
||||
get => _description;
|
||||
set
|
||||
{
|
||||
if (_description != value)
|
||||
{
|
||||
_description = value;
|
||||
OnPropertyChanged(nameof(Description));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Версия темы.
|
||||
/// </summary>
|
||||
public string Version
|
||||
{
|
||||
get => _version;
|
||||
set
|
||||
{
|
||||
if (_version != value)
|
||||
{
|
||||
_version = value;
|
||||
OnPropertyChanged(nameof(Version));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Определяет, является ли тема тёмной.
|
||||
/// </summary>
|
||||
public bool IsDark
|
||||
{
|
||||
get => _isDark;
|
||||
set
|
||||
{
|
||||
if (_isDark != value)
|
||||
{
|
||||
_isDark = value;
|
||||
OnPropertyChanged(nameof(IsDark));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Возвращает список словарей ресурсов, которые должны быть подключены.
|
||||
/// </summary>
|
||||
public abstract IReadOnlyList<Uri> GetResourceUris();
|
||||
|
||||
/// <summary>
|
||||
/// Вызывается при применении темы.
|
||||
/// </summary>
|
||||
public virtual void OnApply()
|
||||
{
|
||||
// Может быть переопределено в производных классах
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызывается при удалении темы.
|
||||
/// </summary>
|
||||
public virtual void OnRemove()
|
||||
{
|
||||
// Может быть переопределено в производных классах
|
||||
}
|
||||
|
||||
protected virtual void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
18
Lattice.Themes.Core/WindowTracker.cs
Normal file
18
Lattice.Themes.Core/WindowTracker.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Lattice.Themes;
|
||||
|
||||
public static class WindowTracker
|
||||
{
|
||||
private static readonly List<Window> _windows = new();
|
||||
|
||||
public static IReadOnlyList<Window> Windows => _windows;
|
||||
|
||||
public static void Register(Window window)
|
||||
{
|
||||
if (!_windows.Contains(window))
|
||||
_windows.Add(window);
|
||||
|
||||
window.Closed += (_, _) => _windows.Remove(window);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user