rearranging the settings window layout

This commit is contained in:
2026-08-09 20:25:56 +05:00
parent 163b86d239
commit 7ecad05e1c
2 changed files with 414 additions and 271 deletions
+163 -42
View File
@@ -8,7 +8,7 @@
mc:Ignorable="d" mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=vm:SettingsViewModel}" d:DataContext="{d:DesignInstance Type=vm:SettingsViewModel}"
Title="{Binding Localization[SettingsTitle]}" Title="{Binding Localization[SettingsTitle]}"
Width="560" SizeToContent="Height" MaxHeight="900" Width="1000" SizeToContent="Height" MaxHeight="900"
ResizeMode="CanMinimize" ResizeMode="CanMinimize"
Background="{DynamicResource Theme.WindowBackground}" Background="{DynamicResource Theme.WindowBackground}"
Foreground="{DynamicResource Theme.Foreground}"> Foreground="{DynamicResource Theme.Foreground}">
@@ -16,12 +16,13 @@
<local:EnumToVisibilityConverter x:Key="EnumToVisibility" /> <local:EnumToVisibilityConverter x:Key="EnumToVisibility" />
<local:ColorToBrushConverter x:Key="ColorToBrush" /> <local:ColorToBrushConverter x:Key="ColorToBrush" />
<local:ColorToHexConverter x:Key="ColorToHex" /> <local:ColorToHexConverter x:Key="ColorToHex" />
<BooleanToVisibilityConverter x:Key="BooleanToVisibility" />
<DataTemplate x:Key="EnumOptionTemplate"> <DataTemplate x:Key="EnumOptionTemplate">
<TextBlock Text="{Binding Display}" /> <TextBlock Text="{Binding Display}" />
</DataTemplate> </DataTemplate>
<!-- Образец цвета с подписью: одинаково для фона и для текста --> <!-- A colour swatch with a caption: the same for the background and for the text -->
<DataTemplate x:Key="ColorSwatchTemplate"> <DataTemplate x:Key="ColorSwatchTemplate">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Border Width="32" Height="16" CornerRadius="2" <Border Width="32" Height="16" CornerRadius="2"
@@ -52,12 +53,18 @@
screen entirely and does without scrolling. Scrolling is kept for the case of screen entirely and does without scrolling. Scrolling is kept for the case of
a large system font, with which the content is taller than the monitor after all --> a large system font, with which the content is taller than the monitor after all -->
<ScrollViewer VerticalScrollBarVisibility="Auto" Padding="16"> <ScrollViewer VerticalScrollBarVisibility="Auto" Padding="16">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel> <StackPanel>
<GroupBox Header="{Binding Localization[SectionInterface]}"> <GroupBox Header="{Binding Localization[SectionInterface]}">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="180" /> <ColumnDefinition Width="164" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
@@ -87,7 +94,7 @@
<GroupBox Header="{Binding Localization[SectionPlacement]}"> <GroupBox Header="{Binding Localization[SectionPlacement]}">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="180" /> <ColumnDefinition Width="164" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
@@ -109,7 +116,7 @@
SelectedValuePath="Value" SelectedValuePath="Value"
SelectedValue="{Binding Settings.PlacementMode}" /> SelectedValue="{Binding Settings.PlacementMode}" />
<!-- Режим «у курсора»: своя сторона и свой отступ --> <!-- The "at cursor" mode: a side and an offset of its own -->
<TextBlock Grid.Row="1" Margin="0,12,12,0" <TextBlock Grid.Row="1" Margin="0,12,12,0"
Style="{StaticResource FieldLabel}" Style="{StaticResource FieldLabel}"
Text="{Binding Localization[CursorCornerLabel]}" Text="{Binding Localization[CursorCornerLabel]}"
@@ -144,7 +151,7 @@
Converter={StaticResource EnumToVisibility}, Converter={StaticResource EnumToVisibility},
ConverterParameter=AtCursor}" /> ConverterParameter=AtCursor}" />
<!-- Режим «у каретки»: свои сторона и отступ --> <!-- The "at caret" mode: a side and an offset of its own -->
<TextBlock Grid.Row="3" Margin="0,12,12,0" <TextBlock Grid.Row="3" Margin="0,12,12,0"
Style="{StaticResource FieldLabel}" Style="{StaticResource FieldLabel}"
Text="{Binding Localization[CursorCornerLabel]}" Text="{Binding Localization[CursorCornerLabel]}"
@@ -179,7 +186,7 @@
Converter={StaticResource EnumToVisibility}, Converter={StaticResource EnumToVisibility},
ConverterParameter=AtCaret}" /> ConverterParameter=AtCaret}" />
<!-- Режим «фиксированная точка» --> <!-- The "fixed point" mode -->
<TextBlock Grid.Row="5" Margin="0,12,12,0" <TextBlock Grid.Row="5" Margin="0,12,12,0"
Style="{StaticResource FieldLabel}" Style="{StaticResource FieldLabel}"
Text="{Binding Localization[ScreenPositionLabel]}" Text="{Binding Localization[ScreenPositionLabel]}"
@@ -216,10 +223,106 @@
</Grid> </Grid>
</GroupBox> </GroupBox>
<GroupBox Header="{Binding Localization[SectionBehavior]}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="164" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Style="{StaticResource FieldLabel}"
Text="{Binding Localization[DurationLabel]}" />
<Slider Grid.Column="1" Minimum="200" Maximum="5000" TickFrequency="100"
Value="{Binding Settings.DurationMilliseconds}" />
<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="{DynamicResource Theme.SecondaryForeground}"
Text="{Binding Localization[MillisecondsSuffix]}" />
</StackPanel>
<TextBlock Grid.Row="1" Margin="0,12,12,0"
Style="{StaticResource FieldLabel}"
Text="{Binding Localization[CapsLockLabel]}" />
<CheckBox Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,12,0,0"
IsChecked="{Binding Settings.UseCapsLockHotkey}"
Content="{Binding Localization[CapsLockHotkeyCheck]}" />
<TextBlock Grid.Row="2" Margin="0,12,12,0"
Style="{StaticResource FieldLabel}"
Text="{Binding Localization[CapsLockHoldLabel]}" />
<Slider Grid.Row="2" Grid.Column="1" Margin="0,12,0,0"
Minimum="150" Maximum="1500" TickFrequency="50"
Value="{Binding Settings.CapsLockHoldMilliseconds}"
IsEnabled="{Binding Settings.UseCapsLockHotkey}" />
<StackPanel Grid.Row="2" Grid.Column="2" Margin="0,12,0,0"
Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Style="{StaticResource FieldValue}"
Text="{Binding Settings.CapsLockHoldMilliseconds, StringFormat={}{0:F0}}" />
<TextBlock Margin="4,0,0,0"
Foreground="{DynamicResource Theme.SecondaryForeground}"
Text="{Binding Localization[MillisecondsSuffix]}" />
</StackPanel>
<TextBlock Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2"
Margin="0,6,0,0" TextWrapping="Wrap"
Foreground="{DynamicResource Theme.SecondaryForeground}">
<Run Text="{Binding Localization[CapsLockHoldHint], Mode=OneWay}" />
<InlineUIContainer BaselineAlignment="Baseline">
<!-- The elevation caveat lives in the tooltip to keep the section compact -->
<TextBlock Text="{Binding Localization[MoreInfoLink]}"
Foreground="{DynamicResource Theme.Accent}"
TextDecorations="Underline"
Cursor="Help"
ToolTipService.InitialShowDelay="200"
ToolTipService.ShowDuration="60000"
Visibility="{Binding Settings.UseCapsLockHotkey, Converter={StaticResource BooleanToVisibility}}">
<TextBlock.ToolTip>
<ToolTip>
<TextBlock TextWrapping="Wrap"
Text="{Binding Localization[CapsLockElevationHint]}" />
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
</InlineUIContainer>
</TextBlock>
<TextBlock Grid.Row="4" Margin="0,12,12,0"
Style="{StaticResource FieldLabel}"
Visibility="{Binding IsStartupAvailable, Converter={StaticResource BooleanToVisibility}}"
Text="{Binding Localization[StartupLabel]}" />
<CheckBox Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,12,0,0"
Visibility="{Binding IsStartupAvailable, Converter={StaticResource BooleanToVisibility}}"
IsEnabled="{Binding CanChangeStartup}"
IsChecked="{Binding RunAtStartup}"
Content="{Binding Localization[StartupCheck]}" />
<TextBlock Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="2"
Margin="0,6,0,0" TextWrapping="Wrap"
Foreground="{DynamicResource Theme.SecondaryForeground}"
Visibility="{Binding IsStartupLocked, Converter={StaticResource BooleanToVisibility}}"
Text="{Binding Localization[StartupLockedHint]}" />
</Grid>
</GroupBox>
</StackPanel>
<StackPanel Grid.Column="1" Margin="16,0,0,0">
<GroupBox Header="{Binding Localization[SectionAppearance]}"> <GroupBox Header="{Binding Localization[SectionAppearance]}">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="180" /> <ColumnDefinition Width="164" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
@@ -267,9 +370,12 @@
<TextBlock Grid.Row="4" Margin="0,12,12,0" <TextBlock Grid.Row="4" Margin="0,12,12,0"
Style="{StaticResource FieldLabel}" Style="{StaticResource FieldLabel}"
Text="{Binding Localization[PreviewLabel]}" /> Text="{Binding Localization[PreviewLabel]}" />
<!-- The height of the area is sized for the largest font
available: the sample text is fully visible at any
position of the slider -->
<Border Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="2" <Border Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="2"
Margin="0,12,0,0" Padding="16" Margin="0,12,0,0" Padding="16"
Height="144" ClipToBounds="True" Height="152" ClipToBounds="True"
Background="{DynamicResource Theme.SurfaceStrong}" CornerRadius="4" Background="{DynamicResource Theme.SurfaceStrong}" CornerRadius="4"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<Border CornerRadius="4" Padding="10,4" <Border CornerRadius="4" Padding="10,4"
@@ -286,10 +392,12 @@
</Grid> </Grid>
</GroupBox> </GroupBox>
<GroupBox Header="{Binding Localization[SectionBehavior]}"> <!-- The section is absent for an app from the Store: the Store updates it itself -->
<GroupBox Header="{Binding Localization[SectionUpdates]}"
Visibility="{Binding Updates.IsSupported, Converter={StaticResource BooleanToVisibility}}">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="180" /> <ColumnDefinition Width="164" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
@@ -298,50 +406,63 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock Style="{StaticResource FieldLabel}" <TextBlock Style="{StaticResource FieldLabel}"
Text="{Binding Localization[DurationLabel]}" /> Text="{Binding Localization[CurrentVersionLabel]}" />
<Slider Grid.Column="1" Minimum="200" Maximum="5000" TickFrequency="100" <TextBlock Grid.Column="1" VerticalAlignment="Center"
Value="{Binding Settings.DurationMilliseconds}" /> Text="{Binding Updates.CurrentVersion}" />
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center"> <Button Grid.Column="2" Padding="12,4"
<TextBlock Style="{StaticResource FieldValue}" Command="{Binding Updates.CheckCommand}"
Text="{Binding Settings.DurationMilliseconds, StringFormat={}{0:F0}}" /> IsEnabled="{Binding Updates.CanCheck}"
<TextBlock Margin="4,0,0,0" Content="{Binding Localization[CheckUpdatesButton]}" />
Foreground="{DynamicResource Theme.SecondaryForeground}"
Text="{Binding Localization[MillisecondsSuffix]}" />
</StackPanel>
<TextBlock Grid.Row="1" Margin="0,12,12,0"
Style="{StaticResource FieldLabel}"
Text="{Binding Localization[CapsLockLabel]}" />
<CheckBox Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,12,0,0" <CheckBox Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,12,0,0"
IsChecked="{Binding Settings.UseCapsLockHotkey}" IsChecked="{Binding Updates.CheckAutomatically}"
Content="{Binding Localization[CapsLockHotkeyCheck]}" /> Content="{Binding Localization[UpdateAutoCheck]}" />
<TextBlock Grid.Row="2" Margin="0,12,12,0" <TextBlock Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2"
Style="{StaticResource FieldLabel}" Margin="0,12,0,0" TextWrapping="Wrap"
Text="{Binding Localization[CapsLockHoldLabel]}" /> Visibility="{Binding Updates.HasStatus, Converter={StaticResource BooleanToVisibility}}"
<Slider Grid.Row="2" Grid.Column="1" Margin="0,12,0,0" Text="{Binding Updates.StatusText}" />
Minimum="150" Maximum="1500" TickFrequency="50"
Value="{Binding Settings.CapsLockHoldMilliseconds}" <!-- The downloaded fraction is not always known: not every hosting reports the file size -->
IsEnabled="{Binding Settings.UseCapsLockHotkey}" /> <ProgressBar Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2"
<StackPanel Grid.Row="2" Grid.Column="2" Margin="0,12,0,0" Margin="0,8,0,0" Height="4" Maximum="1"
Orientation="Horizontal" VerticalAlignment="Center"> Value="{Binding Updates.Progress, Mode=OneWay}"
<TextBlock Style="{StaticResource FieldValue}" IsIndeterminate="{Binding Updates.IsProgressUnknown}"
Text="{Binding Settings.CapsLockHoldMilliseconds, StringFormat={}{0:F0}}" /> Visibility="{Binding Updates.IsProgressShown, Converter={StaticResource BooleanToVisibility}}" />
<TextBlock Margin="4,0,0,0"
Foreground="{DynamicResource Theme.SecondaryForeground}" <StackPanel Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="2"
Text="{Binding Localization[MillisecondsSuffix]}" /> Margin="0,12,0,0" Orientation="Horizontal">
<Button Padding="12,4"
Command="{Binding Updates.DownloadCommand}"
Visibility="{Binding Updates.IsDownloadOffered, Converter={StaticResource BooleanToVisibility}}"
Content="{Binding Localization[DownloadUpdateButton]}" />
<Button Padding="12,4"
Command="{Binding Updates.InstallCommand}"
Visibility="{Binding Updates.IsInstallOffered, Converter={StaticResource BooleanToVisibility}}"
Content="{Binding Localization[InstallUpdateButton]}" />
<TextBlock Margin="12,0,0,0" VerticalAlignment="Center"
Visibility="{Binding Updates.IsReleaseLinkShown, Converter={StaticResource BooleanToVisibility}}">
<Hyperlink NavigateUri="{Binding Updates.ReleaseUrl}"
RequestNavigate="OnReleaseLinkNavigate">
<Run Text="{Binding Localization[ReleasePageLink], Mode=OneWay}" />
</Hyperlink>
</TextBlock>
</StackPanel> </StackPanel>
<TextBlock Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" <TextBlock Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="2"
Margin="0,6,0,0" TextWrapping="Wrap" Margin="0,6,0,0" TextWrapping="Wrap"
Foreground="{DynamicResource Theme.SecondaryForeground}" Foreground="{DynamicResource Theme.SecondaryForeground}"
Text="{Binding Localization[CapsLockHoldHint]}" /> Visibility="{Binding Updates.IsInstallOffered, Converter={StaticResource BooleanToVisibility}}"
Text="{Binding Localization[UpdateInstallHint]}" />
</Grid> </Grid>
</GroupBox> </GroupBox>
</StackPanel> </StackPanel>
</Grid>
</ScrollViewer> </ScrollViewer>
</Window> </Window>
+22
View File
@@ -1,4 +1,7 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Windows; using System.Windows;
using System.Windows.Navigation;
using CursorLang.Services; using CursorLang.Services;
using CursorLang.ViewModels; using CursorLang.ViewModels;
@@ -16,4 +19,23 @@ public partial class MainWindow : Window
theme.Register(this); theme.Register(this);
placement.Attach(this); placement.Attach(this);
} }
/// <summary>
/// Opens the release page in a browser. A link in WPF leads nowhere on its own:
/// where to hand it over is up to the application.
/// </summary>
private void OnReleaseLinkNavigate(object sender, RequestNavigateEventArgs e)
{
e.Handled = true;
try
{
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true })?.Dispose();
}
catch (Exception exception) when (exception is Win32Exception or InvalidOperationException)
{
// There is no browser in the system — that does not get in the way of the
// update, which is downloaded by the button next to it anyway
}
}
} }