Add settings, cloud places, and optional archive-content indexing.

Keep official clients in charge of sync while Explorer can group locations, persist UI prefs, and list zip/rar/7z members without extracting them.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-23 12:16:09 +02:00
parent e9aba73552
commit 09a8cfafa3
57 changed files with 3130 additions and 119 deletions

View File

@@ -228,6 +228,15 @@
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="ToolbarIconButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Width" Value="36"/>
<Setter Property="MinWidth" Value="36"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="FontFamily" Value="{StaticResource Symbol}"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Margin" Value="0,0,4,0"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
<Setter Property="Background" Value="{DynamicResource InputBg}"/>
@@ -360,6 +369,15 @@
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style TargetType="RadioButton">
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style TargetType="Menu">
<Setter Property="Background" Value="{DynamicResource Panel}"/>
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
<Setter Property="FontSize" Value="13"/>
</Style>
<Style TargetType="ContextMenu">
<Setter Property="Background" Value="{DynamicResource Panel}"/>
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
@@ -398,7 +416,11 @@
<Setter.Value>
<ControlTemplate TargetType="MenuItem">
<Border x:Name="Bd" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}">
<ContentPresenter ContentSource="Header" RecognizesAccessKey="True"/>
<DockPanel>
<TextBlock DockPanel.Dock="Right" Text="{TemplateBinding InputGestureText}"
Foreground="{DynamicResource FgMuted}" Margin="24,0,0,0" VerticalAlignment="Center"/>
<ContentPresenter ContentSource="Header" RecognizesAccessKey="True" VerticalAlignment="Center"/>
</DockPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
@@ -411,6 +433,58 @@
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Role" Value="TopLevelHeader">
<Setter Property="Padding" Value="10,6"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="MenuItem">
<Border x:Name="Bd" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}">
<Grid>
<ContentPresenter ContentSource="Header" RecognizesAccessKey="True" VerticalAlignment="Center"/>
<Popup x:Name="PART_Popup"
IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}"
Placement="Bottom"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Fade">
<Border MinWidth="180" Background="{DynamicResource Panel}"
BorderBrush="{DynamicResource Stroke}" BorderThickness="1" Padding="4">
<StackPanel IsItemsHost="True"/>
</Border>
</Popup>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource FillHover}"/>
</Trigger>
<Trigger Property="IsSubmenuOpen" Value="True">
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource FillHover}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="Role" Value="TopLevelItem">
<Setter Property="Padding" Value="10,6"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="MenuItem">
<Border x:Name="Bd" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}">
<ContentPresenter ContentSource="Header" RecognizesAccessKey="True" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource FillHover}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="ProgressBar">
<Setter Property="Height" Value="4"/>

View File

@@ -5,6 +5,8 @@ using Explorer.Domain.Abstractions;
using Explorer.FileOperations;
using Explorer.Indexing;
using Explorer.Plugin.Abstractions;
using Explorer.Plugin.GoogleDrive;
using Explorer.Plugin.Nextcloud;
using Explorer.Plugin.OneDrive;
using Explorer.Presentation.ViewModels;
using Explorer.Search;
@@ -34,11 +36,16 @@ public static class AppServices
return new SqliteIndexStore(env.DatabasePath, logger);
});
services.AddSingleton<IStorageProvider, OneDriveStorageProvider>();
services.AddSingleton<IStorageProvider, GoogleDriveStorageProvider>();
services.AddSingleton<IStorageProvider, NextcloudStorageProvider>();
services.AddSingleton<StorageProviderRegistry>();
services.AddSingleton<IHydrationGuard, HydrationGuard>();
services.AddSingleton<SourceManager>();
services.AddSingleton<PathHistoryStore>();
services.AddSingleton<CloudPlaceStore>();
services.AddSingleton<UiPreferencesStore>();
services.AddSingleton<IArchiveCatalog, ArchiveCatalog>();
services.AddSingleton<ArchiveContentsIndexer>();
services.AddSingleton<BrowseService>();
services.AddSingleton<FilesystemScanner>();
services.AddSingleton<FolderReconciler>();

View File

@@ -29,6 +29,8 @@
<ProjectReference Include="..\Explorer.FileOperations\Explorer.FileOperations.csproj" />
<ProjectReference Include="..\Explorer.Indexing\Explorer.Indexing.csproj" />
<ProjectReference Include="..\Explorer.Plugin.Abstractions\Explorer.Plugin.Abstractions.csproj" />
<ProjectReference Include="..\Explorer.Plugin.GoogleDrive\Explorer.Plugin.GoogleDrive.csproj" />
<ProjectReference Include="..\Explorer.Plugin.Nextcloud\Explorer.Plugin.Nextcloud.csproj" />
<ProjectReference Include="..\Explorer.Plugin.OneDrive\Explorer.Plugin.OneDrive.csproj" />
<ProjectReference Include="..\Explorer.Presentation\Explorer.Presentation.csproj" />
<ProjectReference Include="..\Explorer.Search\Explorer.Search.csproj" />

View File

@@ -41,6 +41,32 @@
</StackPanel>
</Grid>
</Border>
<Menu DockPanel.Dock="Top" Background="{DynamicResource Panel}" Foreground="{DynamicResource Fg}"
BorderBrush="{DynamicResource Stroke}" BorderThickness="0,0,0,1" Padding="4,0">
<MenuItem Header="_File">
<MenuItem Header="New _tab" InputGestureText="Ctrl+T" Command="{Binding NewTabCommand}"/>
<MenuItem Header="_Split pane" Command="{Binding SplitCommand}"/>
<MenuItem Header="_Close tab" InputGestureText="Ctrl+W" Command="{Binding CloseTabCommand}" CommandParameter="{Binding ActiveTab}"/>
</MenuItem>
<MenuItem Header="_View">
<MenuItem Header="_Details" Command="{Binding SetViewCommand}" CommandParameter="Details"/>
<MenuItem Header="_List" Command="{Binding SetViewCommand}" CommandParameter="List"/>
<MenuItem Header="_Preview" Command="{Binding SetViewCommand}" CommandParameter="Preview"/>
<Separator/>
<MenuItem Header="_Refresh" InputGestureText="F5" Command="{Binding RefreshCommand}"/>
</MenuItem>
<MenuItem Header="_Tools">
<MenuItem Header="_Index this location" Command="{Binding BuildIndexCommand}"/>
<MenuItem Header="_Storage" Command="{Binding Analysis.OpenCommand}"/>
<MenuItem Header="_Duplicates" Command="{Binding Duplicates.OpenCommand}"/>
<Separator/>
<MenuItem Header="Add _network…" Click="OnAddNetwork"/>
<MenuItem Header="Add One_Drive…" Click="OnAddOneDrive"/>
<MenuItem Header="Add _Google Drive…" Click="OnAddGoogleDrive"/>
<MenuItem Header="Add Ne_xtcloud…" Click="OnAddNextcloud"/>
</MenuItem>
<MenuItem Header="_Settings" Click="OnOpenSettings"/>
</Menu>
<Border DockPanel.Dock="Top" Background="{DynamicResource Panel}" BorderBrush="{DynamicResource Stroke}" BorderThickness="0,0,0,1" Padding="8">
<Grid>
<Grid.ColumnDefinitions>
@@ -54,12 +80,49 @@
<Button Content="→" Command="{Binding ForwardCommand}" Width="36" Margin="0,0,4,0" ToolTip="Forward (Alt+Right)"/>
<Button Content="↑" Command="{Binding UpCommand}" Width="36" Margin="0,0,4,0" ToolTip="Up (Alt+Up)"/>
<Button Content="↻" Command="{Binding RefreshCommand}" Width="36" Margin="0,0,12,0" ToolTip="Refresh (F5)"/>
<Button Content="New tab" Command="{Binding NewTabCommand}" Margin="0,0,4,0"/>
<Button Content="Split" Command="{Binding SplitCommand}" Margin="0,0,4,0"/>
<Button Content="Index" Command="{Binding BuildIndexCommand}" Margin="0,0,4,0"/>
<Button Content="Details" Command="{Binding SetViewCommand}" CommandParameter="Details" Margin="8,0,4,0"/>
<Button Content="List" Command="{Binding SetViewCommand}" CommandParameter="List" Margin="0,0,4,0"/>
<Button Content="Preview" Command="{Binding SetViewCommand}" CommandParameter="Preview"/>
<Button Style="{StaticResource ToolbarIconButton}" Content="&#xE8C8;"
Command="{Binding NewTabCommand}" ToolTip="New tab (Ctrl+T)"/>
<Button Style="{StaticResource ToolbarIconButton}" Content="&#xE89F;"
Command="{Binding SplitCommand}" ToolTip="Split pane"/>
<Button Style="{StaticResource ToolbarIconButton}" Content="&#xE721;"
Command="{Binding BuildIndexCommand}" ToolTip="Index this location"/>
<Border Width="1" Height="18" Margin="4,0,8,0" VerticalAlignment="Center" Background="{DynamicResource Stroke}"/>
<Button Content="&#xE8EC;"
Command="{Binding SetViewCommand}" CommandParameter="Details" ToolTip="Details">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource ToolbarIconButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding ActivePane.ViewMode}" Value="Details">
<Setter Property="Background" Value="{DynamicResource FillHover}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Content="&#xE8FD;"
Command="{Binding SetViewCommand}" CommandParameter="List" ToolTip="List">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource ToolbarIconButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding ActivePane.ViewMode}" Value="List">
<Setter Property="Background" Value="{DynamicResource FillHover}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Content="&#xE8B9;"
Command="{Binding SetViewCommand}" CommandParameter="Preview" ToolTip="Preview">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource ToolbarIconButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding ActivePane.ViewMode}" Value="Preview">
<Setter Property="Background" Value="{DynamicResource FillHover}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
<ComboBox Grid.Column="1" Margin="12,0" Style="{StaticResource PathComboBox}"
ItemsSource="{Binding PathHistory}"
@@ -69,8 +132,7 @@
<TextBox Grid.Column="2" Text="{Binding Search.Text, UpdateSourceTrigger=PropertyChanged}"
KeyDown="OnSearchKeyDown"/>
<StackPanel Grid.Column="3" Orientation="Horizontal" Margin="8,0,0,0">
<Button Content="Search" Command="{Binding SearchCommand}" Margin="0,0,4,0"/>
<Button Content="Theme" Command="{Binding ToggleThemeCommand}"/>
<Button Content="Search" Command="{Binding SearchCommand}"/>
</StackPanel>
</Grid>
</Border>
@@ -102,7 +164,9 @@
<Button Content="Storage" Command="{Binding Analysis.OpenCommand}" Margin="0,0,6,0"/>
<Button Content="Duplicates" Command="{Binding Duplicates.OpenCommand}" Margin="0,0,6,0"/>
<Button Content="Add network" Click="OnAddNetwork" Margin="0,0,6,0"/>
<Button Content="Add OneDrive" Click="OnAddOneDrive"/>
<Button Content="Add OneDrive" Click="OnAddOneDrive" Margin="0,0,6,0"/>
<Button Content="Add Google Drive" Click="OnAddGoogleDrive" Margin="0,0,6,0"/>
<Button Content="Add Nextcloud" Click="OnAddNextcloud"/>
</StackPanel>
</Grid>
</Border>
@@ -118,7 +182,13 @@
<TreeView x:Name="NavTree" ItemsSource="{Binding Tree.Roots}"
SelectedItemChanged="OnTreeSelected"
TreeViewItem.Expanded="OnTreeExpanded"
ContextMenuOpening="OnTreeContextOpening"
HorizontalContentAlignment="Stretch">
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem x:Name="RemoveLocationMenu" Header="Remove from Explorer" Click="OnRemoveLocation"/>
</ContextMenu>
</TreeView.ContextMenu>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
@@ -206,6 +276,9 @@
<Separator/>
<MenuItem Header="Refresh" Command="{Binding DataContext.RefreshCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
<MenuItem Header="Rescan folder" Command="{Binding DataContext.RescanFolderCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
<Separator Visibility="{Binding DataContext.ShowForgetSource, RelativeSource={RelativeSource AncestorType=Window}, Converter={StaticResource BoolVis}}"/>
<MenuItem Header="Remove from Explorer" Click="OnRemoveLocation"
Visibility="{Binding DataContext.ShowForgetSource, RelativeSource={RelativeSource AncestorType=Window}, Converter={StaticResource BoolVis}}"/>
<Separator Visibility="{Binding DataContext.ShowCloudPin, RelativeSource={RelativeSource AncestorType=Window}, Converter={StaticResource BoolVis}}"/>
<MenuItem Header="Always keep on this device"
Command="{Binding DataContext.PinCloudCommand, RelativeSource={RelativeSource AncestorType=Window}}"

View File

@@ -3,6 +3,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using Explorer.Domain;
using Explorer.Presentation;
using Explorer.Presentation.ViewModels;
@@ -129,6 +130,70 @@ public partial class MainWindow : Window
}
}
private void OnTreeContextOpening(object sender, ContextMenuEventArgs e)
{
var node = FindTreeNode(e.OriginalSource as DependencyObject);
if (node is null || !node.CanRemove)
{
e.Handled = true;
return;
}
node.IsSelected = true;
RemoveLocationMenu.Tag = node.Path;
}
private async void OnRemoveLocation(object sender, RoutedEventArgs e)
{
var path = (sender as FrameworkElement)?.Tag as string
?? RemoveLocationMenu.Tag as string
?? Vm.ActivePane.SelectedItems.FirstOrDefault()?.FullPath
?? (Vm.Tree.Roots.SelectMany(FlattenTree).FirstOrDefault(n => n.IsSelected && n.CanRemove)?.Path);
if (string.IsNullOrWhiteSpace(path))
{
return;
}
var label = Vm.Tree.Roots.SelectMany(FlattenTree).FirstOrDefault(n => NavigationTreeViewModel.PathsEqual(n.Path, path))?.Label
?? path;
var confirm = MessageBox.Show(
this,
$"Remove “{label}” from Explorer?\n\nThe link and its index data will be deleted. Files on disk or the server are not touched.\n\nIf Windows still has this location, it will appear again.",
"Remove from Explorer",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (confirm != MessageBoxResult.Yes)
{
return;
}
await Vm.ForgetSourceAsync(path).ConfigureAwait(true);
}
private static IEnumerable<NavNodeViewModel> FlattenTree(NavNodeViewModel node)
{
yield return node;
foreach (var child in node.Children.Where(c => !c.IsPlaceholder).SelectMany(FlattenTree))
{
yield return child;
}
}
private static NavNodeViewModel? FindTreeNode(DependencyObject? origin)
{
while (origin is not null)
{
if (origin is TreeViewItem { DataContext: NavNodeViewModel node })
{
return node;
}
origin = origin is Visual ? VisualTreeHelper.GetParent(origin) : LogicalTreeHelper.GetParent(origin);
}
return null;
}
private async void OnTreeExpanded(object sender, RoutedEventArgs e)
{
if (e.OriginalSource is TreeViewItem { DataContext: NavNodeViewModel node })
@@ -442,6 +507,13 @@ public partial class MainWindow : Window
}
}
private async void OnOpenSettings(object sender, RoutedEventArgs e)
{
var dlg = new SettingsWindow(Vm) { Owner = this };
dlg.ShowDialog();
await Vm.RefreshForgetActionAsync().ConfigureAwait(true);
}
private void OnAddNetwork(object sender, RoutedEventArgs e)
{
var path = PromptWindow.Ask(this, "Add network location", "Network path (\\\\server\\share):", @"\\");
@@ -453,10 +525,19 @@ public partial class MainWindow : Window
}
private async void OnAddOneDrive(object sender, RoutedEventArgs e)
=> await AddCloudFolderAsync("Add OneDrive folder", MainViewModel.OneDriveProviderId).ConfigureAwait(true);
private async void OnAddGoogleDrive(object sender, RoutedEventArgs e)
=> await AddCloudFolderAsync("Add Google Drive folder", MainViewModel.GoogleDriveProviderId).ConfigureAwait(true);
private async void OnAddNextcloud(object sender, RoutedEventArgs e)
=> await AddCloudFolderAsync("Add Nextcloud folder", MainViewModel.NextcloudProviderId).ConfigureAwait(true);
private async Task AddCloudFolderAsync(string title, string providerId)
{
var picker = new Microsoft.Win32.OpenFolderDialog
{
Title = "Add OneDrive folder",
Title = title,
Multiselect = false
};
if (picker.ShowDialog(this) != true || string.IsNullOrWhiteSpace(picker.FolderName))
@@ -464,7 +545,7 @@ public partial class MainWindow : Window
return;
}
await Vm.AddCloudFolderAsync(picker.FolderName).ConfigureAwait(true);
await Vm.AddCloudFolderAsync(picker.FolderName, providerId).ConfigureAwait(true);
}
private async void OnPreviewKeyDown(object sender, KeyEventArgs e)

View File

@@ -0,0 +1,49 @@
<Window x:Class="Explorer.App.SettingsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Settings"
Icon="pack://application:,,,/Assets/explorer-workbench.ico"
Height="620" Width="560"
MinHeight="520" MinWidth="480"
WindowStartupLocation="CenterOwner"
Background="{DynamicResource Bg}" Foreground="{DynamicResource Fg}"
ResizeMode="NoResize">
<DockPanel Margin="20">
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,20,0,0">
<Button Content="OK" MinWidth="88" Height="32" IsDefault="True" Click="OnOk" Margin="0,0,8,0"/>
<Button Content="Cancel" MinWidth="88" Height="32" IsCancel="True" Click="OnCancel"/>
</StackPanel>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Text="Appearance" FontSize="16" FontWeight="SemiBold" Margin="0,0,0,10"/>
<TextBlock Text="Theme" Foreground="{DynamicResource FgMuted}" Margin="0,0,0,8"/>
<StackPanel Orientation="Horizontal" Margin="0,0,0,20">
<RadioButton x:Name="ThemeDark" Content="Dark" GroupName="Theme" Margin="0,0,16,0"
Checked="OnThemeChanged"/>
<RadioButton x:Name="ThemeLight" Content="Light" GroupName="Theme"
Checked="OnThemeChanged"/>
</StackPanel>
<TextBlock Text="Locations tree" FontSize="16" FontWeight="SemiBold" Margin="0,8,0,10"/>
<TextBlock TextWrapping="Wrap" Foreground="{DynamicResource FgMuted}" Margin="0,0,0,12"
Text="Choose whether network and cloud locations appear as their own top-level items, or are collected under a group next to This PC."/>
<CheckBox x:Name="GroupNetwork" Margin="0,0,0,6"
Content="Group network drives under Network"/>
<TextBlock TextWrapping="Wrap" Foreground="{DynamicResource FgMuted}" Margin="24,0,0,14" FontSize="12"
Text="Mapped letters and UNC shares become children of a Network item. This PC then shows only local and removable drives."/>
<CheckBox x:Name="GroupCloud" Margin="0,0,0,6"
Content="Group cloud locations under Cloud"/>
<TextBlock TextWrapping="Wrap" Foreground="{DynamicResource FgMuted}" Margin="24,0,0,18" FontSize="12"
Text="OneDrive, Google Drive, and Nextcloud become children of a Cloud item. The two options are independent."/>
<TextBlock Text="Indexing" FontSize="16" FontWeight="SemiBold" Margin="0,8,0,10"/>
<CheckBox x:Name="IndexArchives" Margin="0,0,0,6"
Content="Include archive contents in the index"/>
<TextBlock TextWrapping="Wrap" Foreground="{DynamicResource FgMuted}" Margin="24,0,0,0" FontSize="12"
Text="When enabled, a scan lists files inside ZIP, RAR, 7z, TAR, and similar archives from the archive catalog — files are not extracted. Individual uncompressed sizes are stored. Folder totals still use the archives size on disk. Online-only cloud archives are skipped."/>
</StackPanel>
</ScrollViewer>
</DockPanel>
</Window>

View File

@@ -0,0 +1,51 @@
using System.Windows;
using Explorer.Application;
using Explorer.Presentation.ViewModels;
namespace Explorer.App;
public partial class SettingsWindow : Window
{
private readonly MainViewModel _vm;
private readonly string _originalTheme;
public SettingsWindow(MainViewModel vm)
{
InitializeComponent();
_vm = vm;
var prefs = vm.CurrentPreferences();
_originalTheme = prefs.Theme;
ThemeDark.IsChecked = prefs.Theme != "Light";
ThemeLight.IsChecked = prefs.Theme == "Light";
GroupNetwork.IsChecked = prefs.GroupNetworkPlaces;
GroupCloud.IsChecked = prefs.GroupCloudPlaces;
IndexArchives.IsChecked = prefs.IndexArchiveContents;
}
private void OnThemeChanged(object sender, RoutedEventArgs e)
{
if (!IsLoaded)
{
return;
}
_vm.Theme = ThemeLight.IsChecked == true ? "Light" : "Dark";
}
private async void OnOk(object sender, RoutedEventArgs e)
{
var prefs = new UiPreferences(
ThemeLight.IsChecked == true ? "Light" : "Dark",
GroupNetwork.IsChecked == true,
GroupCloud.IsChecked == true,
IndexArchives.IsChecked == true);
await _vm.ApplyPreferencesAsync(prefs).ConfigureAwait(true);
DialogResult = true;
Close();
}
private void OnCancel(object sender, RoutedEventArgs e)
{
_vm.Theme = _originalTheme;
}
}