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:
@@ -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"/>
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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=""
|
||||
Command="{Binding NewTabCommand}" ToolTip="New tab (Ctrl+T)"/>
|
||||
<Button Style="{StaticResource ToolbarIconButton}" Content=""
|
||||
Command="{Binding SplitCommand}" ToolTip="Split pane"/>
|
||||
<Button Style="{StaticResource ToolbarIconButton}" Content=""
|
||||
Command="{Binding BuildIndexCommand}" ToolTip="Index this location"/>
|
||||
<Border Width="1" Height="18" Margin="4,0,8,0" VerticalAlignment="Center" Background="{DynamicResource Stroke}"/>
|
||||
<Button Content=""
|
||||
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=""
|
||||
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=""
|
||||
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}}"
|
||||
|
||||
@@ -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)
|
||||
|
||||
49
src/Explorer.App/SettingsWindow.xaml
Normal file
49
src/Explorer.App/SettingsWindow.xaml
Normal 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 archive’s size on disk. Online-only cloud archives are skipped."/>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
51
src/Explorer.App/SettingsWindow.xaml.cs
Normal file
51
src/Explorer.App/SettingsWindow.xaml.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user