added interface themes

This commit is contained in:
2026-08-09 00:09:47 +05:00
parent 8a19cb6a9c
commit b581d560b0
16 changed files with 607 additions and 19 deletions
+5 -1
View File
@@ -3,6 +3,10 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ShutdownMode="OnMainWindowClose">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/Controls.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
+5
View File
@@ -22,6 +22,8 @@ public partial class App : Application
ConfigureServices(services);
_services = services.BuildServiceProvider();
_services.GetRequiredService<ThemeService>();
MainWindow = _services.GetRequiredService<MainWindow>();
MainWindow.Show();
@@ -43,6 +45,9 @@ public partial class App : Application
services.AddSingleton<SettingsService>();
services.AddSingleton(provider => provider.GetRequiredService<SettingsService>().Load());
services.AddSingleton<ThemeService>();
services.AddSingleton<IThemeService>(provider => provider.GetRequiredService<ThemeService>());
services.AddSingleton<ILocalizationService, LocalizationService>();
services.AddSingleton<IKeyboardLayoutService, KeyboardLayoutService>();
services.AddSingleton<ILayoutPopupService, LayoutPopupService>();
+30
View File
@@ -0,0 +1,30 @@
using System.Runtime.InteropServices;
namespace CursorLang.Interop;
/// <summary>
/// Оформление рамки окна средствами системы: заголовок рисует Windows,
/// и в тёмной теме его нужно переключать отдельно от содержимого окна.
/// </summary>
internal static class WindowThemeNative
{
[DllImport("dwmapi.dll")]
private static extern int DwmSetWindowAttribute(IntPtr hWnd, int attribute, ref int value, int size);
private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
/// <summary>
/// Перекрашивает заголовок окна. На сборках Windows 10 до 2004 атрибут
/// не поддерживается — заголовок просто останется светлым.
/// </summary>
internal static void SetDarkTitleBar(IntPtr hWnd, bool isDark)
{
if (hWnd == IntPtr.Zero)
{
return;
}
int value = isDark ? 1 : 0;
DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, ref value, sizeof(int));
}
}
+4
View File
@@ -14,6 +14,10 @@ public sealed partial class AppSettings : ObservableObject
[ObservableProperty]
private string _language = "en";
/// <summary>Оформление окна настроек.</summary>
[ObservableProperty]
private AppTheme _theme = AppTheme.System;
[ObservableProperty]
private PopupPlacementMode _placementMode = PopupPlacementMode.AtCursor;
+12
View File
@@ -0,0 +1,12 @@
namespace CursorLang.Models;
/// <summary>
/// Оформление окна настроек. По умолчанию приложение следует теме Windows,
/// но пользователь может закрепить светлую или тёмную.
/// </summary>
public enum AppTheme
{
System,
Light,
Dark
}
+12
View File
@@ -67,6 +67,18 @@
<data name="LanguageLabel" xml:space="preserve">
<value>Interface language</value>
</data>
<data name="ThemeLabel" xml:space="preserve">
<value>Theme</value>
</data>
<data name="AppTheme_System" xml:space="preserve">
<value>Same as Windows</value>
</data>
<data name="AppTheme_Light" xml:space="preserve">
<value>Light</value>
</data>
<data name="AppTheme_Dark" xml:space="preserve">
<value>Dark</value>
</data>
<data name="SectionPlacement" xml:space="preserve">
<value>Placement</value>
</data>
+12
View File
@@ -67,6 +67,18 @@
<data name="LanguageLabel" xml:space="preserve">
<value>Язык интерфейса</value>
</data>
<data name="ThemeLabel" xml:space="preserve">
<value>Тема</value>
</data>
<data name="AppTheme_System" xml:space="preserve">
<value>Как в Windows</value>
</data>
<data name="AppTheme_Light" xml:space="preserve">
<value>Светлая</value>
</data>
<data name="AppTheme_Dark" xml:space="preserve">
<value>Тёмная</value>
</data>
<data name="SectionPlacement" xml:space="preserve">
<value>Расположение</value>
</data>
+19
View File
@@ -0,0 +1,19 @@
using System.Windows;
using CursorLang.Models;
namespace CursorLang.Services;
/// <summary>
/// Применяет светлое или тёмное оформление к окнам приложения.
/// </summary>
public interface IThemeService
{
/// <summary>Тема, действующая сейчас.</summary>
AppTheme CurrentTheme { get; }
/// <summary>
/// Подключает окно к смене темы: заголовок окна рисует Windows,
/// и его цвет приходится переключать для каждого окна отдельно.
/// </summary>
void Register(Window window);
}
-2
View File
@@ -90,12 +90,10 @@ public sealed class SettingsService : IDisposable
}
catch (Exception e) when (e is IOException or UnauthorizedAccessException or JsonException)
{
// Испорченный файл не должен мешать запуску: начнём с умолчаний
return null;
}
}
// Язык по умолчанию берём системный, если он поддерживается
private static AppSettings CreateDefault()
{
string uiLanguage = System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
+144
View File
@@ -0,0 +1,144 @@
using System.ComponentModel;
using System.Windows;
using System.Windows.Interop;
using CursorLang.Interop;
using CursorLang.Models;
using Microsoft.Win32;
namespace CursorLang.Services;
/// <summary>
/// Держит в ресурсах приложения палитру выбранной темы и подменяет её
/// при смене настройки — окна перекрашиваются без перезапуска.
/// </summary>
public sealed class ThemeService : IThemeService, IDisposable
{
private const string PersonalizeKey =
@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize";
private readonly AppSettings _settings;
private readonly List<Window> _windows = [];
private ResourceDictionary? _palette;
private AppTheme _current;
public ThemeService(AppSettings settings)
{
_settings = settings;
_settings.PropertyChanged += OnSettingsChanged;
SystemEvents.UserPreferenceChanged += OnUserPreferenceChanged;
Apply();
}
/// <summary>Тема, которую видит пользователь: <c>System</c> здесь уже разрешён.</summary>
public AppTheme CurrentTheme => _current;
public void Register(Window window)
{
_windows.Add(window);
window.Closed += OnWindowClosed;
if (new WindowInteropHelper(window).Handle == IntPtr.Zero)
{
window.SourceInitialized += OnWindowSourceInitialized;
return;
}
ApplyTitleBar(window);
}
public void Dispose()
{
_settings.PropertyChanged -= OnSettingsChanged;
SystemEvents.UserPreferenceChanged -= OnUserPreferenceChanged;
foreach (Window window in _windows)
{
window.Closed -= OnWindowClosed;
window.SourceInitialized -= OnWindowSourceInitialized;
}
_windows.Clear();
}
/// <summary>Тема приложений в настройках Windows.</summary>
private static AppTheme DetectSystemTheme()
{
try
{
using RegistryKey? key = Registry.CurrentUser.OpenSubKey(PersonalizeKey);
return key?.GetValue("AppsUseLightTheme") is int light && light == 0
? AppTheme.Dark
: AppTheme.Light;
}
catch (Exception e) when (e is System.Security.SecurityException or UnauthorizedAccessException)
{
return AppTheme.Light;
}
}
private void OnSettingsChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(AppSettings.Theme))
{
Apply();
}
}
// Windows сообщает о смене оформления не из потока интерфейса
private void OnUserPreferenceChanged(object? sender, UserPreferenceChangedEventArgs e) =>
Application.Current?.Dispatcher.InvokeAsync(Apply);
private void Apply()
{
AppTheme theme = _settings.Theme == AppTheme.System ? DetectSystemTheme() : _settings.Theme;
if (_palette is not null && theme == _current)
{
return;
}
_current = theme;
var next = new ResourceDictionary
{
Source = new Uri($"pack://application:,,,/Themes/{theme}.xaml", UriKind.Absolute),
};
ICollection<ResourceDictionary> dictionaries = Application.Current.Resources.MergedDictionaries;
dictionaries.Add(next);
if (_palette is not null)
{
dictionaries.Remove(_palette);
}
_palette = next;
foreach (Window window in _windows)
{
ApplyTitleBar(window);
}
}
private void ApplyTitleBar(Window window) =>
WindowThemeNative.SetDarkTitleBar(
new WindowInteropHelper(window).Handle,
_current == AppTheme.Dark);
private void OnWindowSourceInitialized(object? sender, EventArgs e)
{
if (sender is Window window)
{
window.SourceInitialized -= OnWindowSourceInitialized;
ApplyTitleBar(window);
}
}
private void OnWindowClosed(object? sender, EventArgs e)
{
if (sender is Window window)
{
window.Closed -= OnWindowClosed;
_windows.Remove(window);
}
}
}
+294
View File
@@ -0,0 +1,294 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- ======================= GroupBox ======================= -->
<Style TargetType="GroupBox">
<Setter Property="Padding" Value="12" />
<Setter Property="Margin" Value="0,0,0,12" />
<Setter Property="Foreground" Value="{DynamicResource Theme.Foreground}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupBox">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ContentPresenter ContentSource="Header"
Margin="2,0,0,6"
TextBlock.FontWeight="SemiBold" />
<Border Grid.Row="1"
Background="{DynamicResource Theme.Surface}"
BorderBrush="{DynamicResource Theme.ControlBorder}"
BorderThickness="1"
CornerRadius="6"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="True">
<ContentPresenter />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ======================= ComboBox ======================= -->
<Style x:Key="ComboBoxToggleStyle" TargetType="ToggleButton">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Focusable" Value="False" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="ClickMode" Value="Press" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border x:Name="Border"
Background="{DynamicResource Theme.ControlBackground}"
BorderBrush="{DynamicResource Theme.ControlBorder}"
BorderThickness="1"
CornerRadius="4"
SnapsToDevicePixels="True">
<Path x:Name="Arrow"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,10,0"
Data="M0,0 L4,4 L8,0"
Stroke="{DynamicResource Theme.SecondaryForeground}"
StrokeThickness="1.4" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="Background"
Value="{DynamicResource Theme.ControlHoverBackground}" />
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="Border" Property="BorderBrush"
Value="{DynamicResource Theme.Accent}" />
<Setter TargetName="Arrow" Property="Stroke"
Value="{DynamicResource Theme.Accent}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Opacity" Value="0.5" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="ComboBoxItem">
<Setter Property="Foreground" Value="{DynamicResource Theme.Foreground}" />
<Setter Property="Padding" Value="8,5" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border x:Name="Border"
Background="Transparent"
CornerRadius="3"
Margin="3,1"
Padding="{TemplateBinding Padding}">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="Border" Property="Background"
Value="{DynamicResource Theme.SelectionBackground}" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="Background"
Value="{DynamicResource Theme.SelectionBackground}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="ComboBox">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Foreground" Value="{DynamicResource Theme.Foreground}" />
<!-- Правый отступ оставляет место под стрелку -->
<Setter Property="Padding" Value="9,5,28,5" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid>
<ToggleButton Style="{StaticResource ComboBoxToggleStyle}"
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}}" />
<ContentPresenter Margin="{TemplateBinding Padding}"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}"
HorizontalAlignment="Left" VerticalAlignment="Center"
IsHitTestVisible="False" />
<Popup x:Name="PART_Popup"
Placement="Bottom"
IsOpen="{TemplateBinding IsDropDownOpen}"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Fade">
<Border Background="{DynamicResource Theme.PopupBackground}"
BorderBrush="{DynamicResource Theme.ControlBorder}"
BorderThickness="1"
CornerRadius="4"
Margin="0,2,0,0"
MinWidth="{Binding ActualWidth,
RelativeSource={RelativeSource TemplatedParent}}"
MaxHeight="{TemplateBinding MaxDropDownHeight}">
<ScrollViewer>
<ItemsPresenter KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Border>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ======================= Slider ======================= -->
<Style x:Key="SliderThumbStyle" TargetType="Thumb">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Width" Value="14" />
<Setter Property="Height" Value="14" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Thumb">
<Ellipse x:Name="Circle"
Fill="{DynamicResource Theme.Accent}"
Stroke="{DynamicResource Theme.ControlBackground}"
StrokeThickness="2" />
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Circle" Property="StrokeThickness" Value="1" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Пройденная часть шкалы: закрашивается акцентом -->
<Style x:Key="SliderDecreaseStyle" TargetType="RepeatButton">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Focusable" Value="False" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RepeatButton">
<Border Height="4" CornerRadius="2"
VerticalAlignment="Center"
Background="{DynamicResource Theme.Accent}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TransparentRepeatStyle" TargetType="RepeatButton">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Focusable" Value="False" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RepeatButton">
<Border Background="Transparent" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Slider">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="IsSnapToTickEnabled" Value="True" />
<Setter Property="MinHeight" Value="24" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Slider">
<Grid Background="Transparent">
<Border Height="4" CornerRadius="2"
VerticalAlignment="Center"
Background="{DynamicResource Theme.SliderTrack}" />
<Track x:Name="PART_Track">
<Track.DecreaseRepeatButton>
<RepeatButton Command="Slider.DecreaseLarge"
Style="{StaticResource SliderDecreaseStyle}" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource SliderThumbStyle}" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Command="Slider.IncreaseLarge"
Style="{StaticResource TransparentRepeatStyle}" />
</Track.IncreaseRepeatButton>
</Track>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ======================= ScrollBar ======================= -->
<Style x:Key="ScrollBarThumbStyle" TargetType="Thumb">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Thumb">
<Border Background="{DynamicResource Theme.ScrollBarThumb}"
CornerRadius="3" Margin="3" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="ScrollBar">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Width" Value="12" />
<Setter Property="MinWidth" Value="12" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ScrollBar">
<Grid Background="{TemplateBinding Background}">
<Track x:Name="PART_Track"
Orientation="{TemplateBinding Orientation}"
IsDirectionReversed="True">
<Track.DecreaseRepeatButton>
<RepeatButton Command="ScrollBar.PageUpCommand"
Style="{StaticResource TransparentRepeatStyle}" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource ScrollBarThumbStyle}" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Command="ScrollBar.PageDownCommand"
Style="{StaticResource TransparentRepeatStyle}" />
</Track.IncreaseRepeatButton>
</Track>
</Grid>
<ControlTemplate.Triggers>
<!-- У горизонтальной полосы направление трека обычное -->
<Trigger Property="Orientation" Value="Horizontal">
<Setter TargetName="PART_Track" Property="IsDirectionReversed" Value="False" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Orientation" Value="Horizontal">
<Setter Property="Width" Value="Auto" />
<Setter Property="MinWidth" Value="0" />
<Setter Property="Height" Value="12" />
<Setter Property="MinHeight" Value="12" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
+21
View File
@@ -0,0 +1,21 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="Theme.WindowBackground" Color="#1E1E21" />
<SolidColorBrush x:Key="Theme.Foreground" Color="#E8E8EA" />
<SolidColorBrush x:Key="Theme.SecondaryForeground" Color="#9A9AA2" />
<SolidColorBrush x:Key="Theme.Surface" Color="#26262A" />
<SolidColorBrush x:Key="Theme.SurfaceStrong" Color="#303036" />
<SolidColorBrush x:Key="Theme.ControlBackground" Color="#2C2C31" />
<SolidColorBrush x:Key="Theme.ControlHoverBackground" Color="#38383E" />
<SolidColorBrush x:Key="Theme.ControlBorder" Color="#46464C" />
<SolidColorBrush x:Key="Theme.PopupBackground" Color="#2A2A2F" />
<SolidColorBrush x:Key="Theme.SelectionBackground" Color="#3A4665" />
<SolidColorBrush x:Key="Theme.Accent" Color="#5B8CF7" />
<SolidColorBrush x:Key="Theme.SliderTrack" Color="#46464C" />
<SolidColorBrush x:Key="Theme.ScrollBarThumb" Color="#55555C" />
</ResourceDictionary>
+21
View File
@@ -0,0 +1,21 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="Theme.WindowBackground" Color="#FFFFFF" />
<SolidColorBrush x:Key="Theme.Foreground" Color="#1B1B1F" />
<SolidColorBrush x:Key="Theme.SecondaryForeground" Color="#6B6B70" />
<SolidColorBrush x:Key="Theme.Surface" Color="#FAFAFB" />
<SolidColorBrush x:Key="Theme.SurfaceStrong" Color="#EFEFF1" />
<SolidColorBrush x:Key="Theme.ControlBackground" Color="#FFFFFF" />
<SolidColorBrush x:Key="Theme.ControlHoverBackground" Color="#F2F2F4" />
<SolidColorBrush x:Key="Theme.ControlBorder" Color="#D0D0D4" />
<SolidColorBrush x:Key="Theme.PopupBackground" Color="#FFFFFF" />
<SolidColorBrush x:Key="Theme.SelectionBackground" Color="#E4EBFB" />
<SolidColorBrush x:Key="Theme.Accent" Color="#2563EB" />
<SolidColorBrush x:Key="Theme.SliderTrack" Color="#DCDCE0" />
<SolidColorBrush x:Key="Theme.ScrollBarThumb" Color="#C2C2C7" />
</ResourceDictionary>
@@ -62,6 +62,7 @@ public sealed partial class SettingsViewModel : ObservableObject, IDisposable
Settings.PropertyChanged += OnSettingsChanged;
Localization.PropertyChanged += OnLocalizationChanged;
Themes = CreateOptions<AppTheme>();
PlacementModes = CreateOptions<PopupPlacementMode>();
AnchorSides = CreateOptions<AnchorSide>();
ScreenPositions = CreateOptions<ScreenPosition>();
@@ -73,6 +74,8 @@ public sealed partial class SettingsViewModel : ObservableObject, IDisposable
public IReadOnlyList<Color> BackgroundPalette { get; } = Palette;
public IReadOnlyList<EnumOption<AppTheme>> Themes { get; }
public IReadOnlyList<EnumOption<PopupPlacementMode>> PlacementModes { get; }
public IReadOnlyList<EnumOption<AnchorSide>> AnchorSides { get; }
@@ -102,6 +105,7 @@ public sealed partial class SettingsViewModel : ObservableObject, IDisposable
return;
}
Translate(Themes);
Translate(PlacementModes);
Translate(AnchorSides);
Translate(ScreenPositions);
+21 -15
View File
@@ -9,7 +9,9 @@
d:DataContext="{d:DesignInstance Type=vm:SettingsViewModel}"
Title="{Binding Localization[SettingsTitle]}"
Width="560" SizeToContent="Height" MaxHeight="900"
ResizeMode="CanMinimize">
ResizeMode="CanMinimize"
Background="{DynamicResource Theme.WindowBackground}"
Foreground="{DynamicResource Theme.Foreground}">
<Window.Resources>
<local:EnumToVisibilityConverter x:Key="EnumToVisibility" />
<local:ColorToBrushConverter x:Key="ColorToBrush" />
@@ -24,18 +26,7 @@
<Setter Property="Margin" Value="12,0,0,0" />
<Setter Property="MinWidth" Value="48" />
<Setter Property="TextAlignment" Value="Right" />
<Setter Property="Foreground" Value="#666" />
</Style>
<Style TargetType="GroupBox">
<Setter Property="Padding" Value="12" />
<Setter Property="Margin" Value="0,0,0,12" />
</Style>
<Style TargetType="ComboBox">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style TargetType="Slider">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="IsSnapToTickEnabled" Value="True" />
<Setter Property="Foreground" Value="{DynamicResource Theme.SecondaryForeground}" />
</Style>
</Window.Resources>
@@ -48,6 +39,11 @@
<ColumnDefinition Width="180" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Style="{StaticResource FieldLabel}"
Text="{Binding Localization[LanguageLabel]}" />
<ComboBox Grid.Column="1"
@@ -55,6 +51,15 @@
DisplayMemberPath="DisplayName"
SelectedValuePath="Code"
SelectedValue="{Binding Settings.Language}" />
<TextBlock Grid.Row="1" Margin="0,12,12,0"
Style="{StaticResource FieldLabel}"
Text="{Binding Localization[ThemeLabel]}" />
<ComboBox Grid.Row="1" Grid.Column="1" Margin="0,12,0,0"
ItemsSource="{Binding Themes}"
DisplayMemberPath="Display"
SelectedValuePath="Value"
SelectedValue="{Binding Settings.Theme}" />
</Grid>
</GroupBox>
@@ -245,7 +250,7 @@
Text="{Binding Localization[PreviewLabel]}" />
<Border Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2"
Margin="0,12,0,0" Padding="16"
Background="#F0F0F0" CornerRadius="4"
Background="{DynamicResource Theme.SurfaceStrong}" CornerRadius="4"
HorizontalAlignment="Stretch">
<Border CornerRadius="4" Padding="10,4"
HorizontalAlignment="Center"
@@ -273,7 +278,8 @@
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Style="{StaticResource FieldValue}"
Text="{Binding Settings.DurationMilliseconds, StringFormat={}{0:F0}}" />
<TextBlock Margin="4,0,0,0" Foreground="#666"
<TextBlock Margin="4,0,0,0"
Foreground="{DynamicResource Theme.SecondaryForeground}"
Text="{Binding Localization[MillisecondsSuffix]}" />
</StackPanel>
</Grid>
+3 -1
View File
@@ -1,4 +1,5 @@
using System.Windows;
using CursorLang.Services;
using CursorLang.ViewModels;
namespace CursorLang.Views;
@@ -8,9 +9,10 @@ namespace CursorLang.Views;
/// </summary>
public partial class MainWindow : Window
{
public MainWindow(SettingsViewModel viewModel)
public MainWindow(SettingsViewModel viewModel, IThemeService theme)
{
InitializeComponent();
DataContext = viewModel;
theme.Register(this);
}
}