Add Explorer Workbench with hierarchical, off-UI Storage analysis.
Storage queries run in the background with cancellation and covering indexes so switching views no longer freezes the UI. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
850
src/Explorer.App/App.xaml
Normal file
850
src/Explorer.App/App.xaml
Normal file
@@ -0,0 +1,850 @@
|
||||
<Application x:Class="Explorer.App.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Explorer.App"
|
||||
xmlns:shell="clr-namespace:System.Windows.Shell;assembly=PresentationFramework">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Themes/Dark.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<FontFamily x:Key="Symbol">Segoe MDL2 Assets</FontFamily>
|
||||
<BooleanToVisibilityConverter x:Key="BoolVis"/>
|
||||
<local:ActiveThicknessConverter x:Key="ActiveThickness"/>
|
||||
<local:FractionWidthConverter x:Key="FractionWidth"/>
|
||||
<local:IndentConverter x:Key="Indent"/>
|
||||
<local:ViewModeToVisibilityConverter x:Key="ViewDetails" Match="Details"/>
|
||||
<local:ViewModeToVisibilityConverter x:Key="ViewList" Match="List"/>
|
||||
<local:ViewModeToVisibilityConverter x:Key="ViewPreview" Match="Preview"/>
|
||||
<local:ShellIconConverter x:Key="ShellIcon"/>
|
||||
<local:ThumbnailConverter x:Key="Thumbnail"/>
|
||||
<local:TransferActionTextConverter x:Key="TransferAction"/>
|
||||
<DataTemplate x:Key="NameWithIcon">
|
||||
<StackPanel Orientation="Horizontal" ToolTip="{Binding SizeTooltip}">
|
||||
<Image Width="16" Height="16" Margin="0,0,8,0" RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Source="{Binding Converter={StaticResource ShellIcon}}"/>
|
||||
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" Foreground="{DynamicResource Fg}"/>
|
||||
<TextBlock Text="{Binding CloudStatus}" Margin="8,0,0,0" VerticalAlignment="Center" FontSize="11"
|
||||
Foreground="{DynamicResource FgMuted}"
|
||||
Visibility="{Binding HasCloudStatus, Converter={StaticResource BoolVis}}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="PreviewTile">
|
||||
<StackPanel Width="96" Margin="2">
|
||||
<Grid Width="96" Height="96">
|
||||
<Image Width="96" Height="96" Stretch="Uniform"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Source="{Binding Converter={StaticResource Thumbnail}}">
|
||||
<Image.Style>
|
||||
<Style TargetType="Image">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsImage}" Value="True">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Image.Style>
|
||||
</Image>
|
||||
<Image Width="64" Height="64" Stretch="Uniform"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Source="{Binding Converter={StaticResource ShellIcon}, ConverterParameter=large}">
|
||||
<Image.Style>
|
||||
<Style TargetType="Image">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsImage}" Value="True">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Image.Style>
|
||||
</Image>
|
||||
</Grid>
|
||||
<TextBlock Text="{Binding Name}" TextAlignment="Center" TextWrapping="Wrap" TextTrimming="CharacterEllipsis"
|
||||
MaxHeight="36" Margin="0,4,0,0" Foreground="{DynamicResource Fg}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
||||
<Style x:Key="CrumbButton" TargetType="Button">
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="8,3"/>
|
||||
<Setter Property="FontSize" Value="13"/>
|
||||
<Setter Property="SnapsToDevicePixels" Value="True"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border x:Name="Bd" Background="{TemplateBinding Background}" CornerRadius="3"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource FillHover}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<DataTemplate x:Key="PaneAddressBar">
|
||||
<DockPanel LastChildFill="True" Margin="6,6,6,4">
|
||||
<Button DockPanel.Dock="Left" Content="↑" Width="32" Height="28" Margin="0,0,6,0"
|
||||
Style="{StaticResource CrumbButton}"
|
||||
Command="{Binding UpCommand}"
|
||||
IsEnabled="{Binding CanGoUp}"
|
||||
ToolTip="Up one folder"/>
|
||||
<Border Background="{DynamicResource InputBg}" BorderBrush="{DynamicResource Stroke}"
|
||||
BorderThickness="1" CornerRadius="4" Padding="4,0" MinHeight="28">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled"
|
||||
Focusable="False">
|
||||
<ItemsControl ItemsSource="{Binding Breadcrumb}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button Style="{StaticResource CrumbButton}"
|
||||
Command="{Binding DataContext.GoBreadcrumbCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
|
||||
CommandParameter="{Binding}"
|
||||
ToolTip="{Binding Path}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock FontFamily="{StaticResource Symbol}" Text="" FontSize="14"
|
||||
Margin="0,0,6,0" VerticalAlignment="Center" Foreground="{DynamicResource Accent}">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Label}" Value="This PC">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
<TextBlock Text="{Binding Label}" VerticalAlignment="Center" Foreground="{DynamicResource Fg}"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<TextBlock Text="›" Margin="2,0,2,0" VerticalAlignment="Center" Foreground="{DynamicResource FgMuted}">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsLast}" Value="True">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</DockPanel>
|
||||
</DataTemplate>
|
||||
<Style x:Key="PaneChrome" TargetType="Border">
|
||||
<Setter Property="SnapsToDevicePixels" Value="True"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource Stroke}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Background" Value="{DynamicResource Panel}"/>
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding DataContext.IsSplit, RelativeSource={RelativeSource AncestorType=Grid}}" Value="True">
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
</DataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsActive}" Value="True"/>
|
||||
<Condition Binding="{Binding DataContext.IsSplit, RelativeSource={RelativeSource AncestorType=Grid}}" Value="True"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource Accent}"/>
|
||||
<Setter Property="BorderThickness" Value="3,1,1,1"/>
|
||||
<Setter Property="Background" Value="{DynamicResource ActivePaneFill}"/>
|
||||
</MultiDataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ExpandToggle" TargetType="ToggleButton">
|
||||
<Setter Property="Focusable" Value="False"/>
|
||||
<Setter Property="Width" Value="16"/>
|
||||
<Setter Property="Height" Value="16"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ToggleButton">
|
||||
<Border Background="Transparent" Padding="2">
|
||||
<Path x:Name="Arrow"
|
||||
Data="M 3 1 L 9 7 L 3 13 Z"
|
||||
Fill="{DynamicResource FgMuted}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter TargetName="Arrow" Property="Data" Value="M 1 3 L 13 3 L 7 11 Z"/>
|
||||
<Setter TargetName="Arrow" Property="Fill" Value="{DynamicResource Fg}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Arrow" Property="Fill" Value="{DynamicResource Fg}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
<Setter Property="Background" Value="{DynamicResource Fill}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource Stroke}"/>
|
||||
<Setter Property="Padding" Value="10,6"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="SnapsToDevicePixels" Value="True"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="4"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource FillHover}"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
<Setter Property="Background" Value="{DynamicResource InputBg}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource Stroke}"/>
|
||||
<Setter Property="CaretBrush" Value="{DynamicResource Fg}"/>
|
||||
<Setter Property="Padding" Value="8,6"/>
|
||||
<Setter Property="MinHeight" Value="32"/>
|
||||
<Setter Property="FontSize" Value="13"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
</Style>
|
||||
<Style TargetType="ComboBox">
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
<Setter Property="Background" Value="{DynamicResource InputBg}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource Stroke}"/>
|
||||
<Setter Property="MinHeight" Value="32"/>
|
||||
<Setter Property="Padding" Value="8,6"/>
|
||||
<Setter Property="FontSize" Value="13"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
<Setter Property="ItemContainerStyle">
|
||||
<Setter.Value>
|
||||
<Style TargetType="ComboBoxItem">
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
<Setter Property="Background" Value="{DynamicResource Panel}"/>
|
||||
<Setter Property="Padding" Value="8,4"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsHighlighted" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource FillHover}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsSelected" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource ListSelection}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ComboBox">
|
||||
<Grid>
|
||||
<ToggleButton x:Name="Toggle" ClickMode="Press" Focusable="False"
|
||||
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}">
|
||||
<ToggleButton.Template>
|
||||
<ControlTemplate TargetType="ToggleButton">
|
||||
<Border Background="{DynamicResource InputBg}" BorderBrush="{DynamicResource Stroke}" BorderThickness="1" CornerRadius="4">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="22"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Path Grid.Column="1" Data="M 0 0 L 8 0 L 4 5 Z" Fill="{DynamicResource Fg}"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</ToggleButton.Template>
|
||||
</ToggleButton>
|
||||
<ContentPresenter Margin="8,0,28,0" VerticalAlignment="Center" HorizontalAlignment="Left"
|
||||
Content="{TemplateBinding SelectionBoxItem}"
|
||||
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
|
||||
ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}"
|
||||
IsHitTestVisible="False"/>
|
||||
<Popup x:Name="PART_Popup" IsOpen="{TemplateBinding IsDropDownOpen}" Placement="Bottom" AllowsTransparency="True" Focusable="False" PopupAnimation="Slide">
|
||||
<Border MinWidth="{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
MaxHeight="{TemplateBinding MaxDropDownHeight}"
|
||||
Background="{DynamicResource Panel}" BorderBrush="{DynamicResource Stroke}" BorderThickness="1">
|
||||
<ScrollViewer>
|
||||
<ItemsPresenter KeyboardNavigation.DirectionalNavigation="Contained"/>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Popup>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="PathComboBox" TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
|
||||
<Setter Property="IsEditable" Value="True"/>
|
||||
<Setter Property="IsTextSearchEnabled" Value="False"/>
|
||||
<Setter Property="StaysOpenOnEdit" Value="True"/>
|
||||
<Setter Property="MaxDropDownHeight" Value="280"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ComboBox">
|
||||
<Grid>
|
||||
<Border Background="{DynamicResource InputBg}" BorderBrush="{DynamicResource Stroke}"
|
||||
BorderThickness="1" CornerRadius="4"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="26"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox x:Name="PART_EditableTextBox"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Foreground="{DynamicResource Fg}"
|
||||
CaretBrush="{DynamicResource Fg}"
|
||||
Padding="8,6"
|
||||
VerticalContentAlignment="Center"
|
||||
FontSize="13"/>
|
||||
<ToggleButton Grid.Column="1" Focusable="False" ClickMode="Press"
|
||||
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}">
|
||||
<ToggleButton.Template>
|
||||
<ControlTemplate TargetType="ToggleButton">
|
||||
<Border Background="Transparent">
|
||||
<Path Data="M 0 0 L 8 0 L 4 5 Z" Fill="{DynamicResource Fg}"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</ToggleButton.Template>
|
||||
</ToggleButton>
|
||||
</Grid>
|
||||
<Popup x:Name="PART_Popup" IsOpen="{TemplateBinding IsDropDownOpen}" Placement="Bottom"
|
||||
AllowsTransparency="True" Focusable="False" PopupAnimation="Slide">
|
||||
<Border MinWidth="{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
MaxHeight="{TemplateBinding MaxDropDownHeight}"
|
||||
Background="{DynamicResource Panel}" BorderBrush="{DynamicResource Stroke}" BorderThickness="1">
|
||||
<ScrollViewer>
|
||||
<ItemsPresenter KeyboardNavigation.DirectionalNavigation="Contained"/>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Popup>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="CheckBox">
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
<Style TargetType="ContextMenu">
|
||||
<Setter Property="Background" Value="{DynamicResource Panel}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource Stroke}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="4"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ContextMenu">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="Separator">
|
||||
<Setter Property="Background" Value="{DynamicResource Stroke}"/>
|
||||
<Setter Property="Margin" Value="8,4"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Separator">
|
||||
<Border Height="1" Background="{TemplateBinding Background}" Margin="{TemplateBinding Margin}"/>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="MenuItem">
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Padding" Value="12,6"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="MenuItem">
|
||||
<Border x:Name="Bd" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter ContentSource="Header" RecognizesAccessKey="True"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsHighlighted" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource FillHover}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Foreground" Value="{DynamicResource FgMuted}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="ProgressBar">
|
||||
<Setter Property="Height" Value="4"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource Accent}"/>
|
||||
<Setter Property="Background" Value="{DynamicResource Fill}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
</Style>
|
||||
<Style x:Key="StorageSizeBar" TargetType="ProgressBar" BasedOn="{StaticResource {x:Type ProgressBar}}">
|
||||
<Setter Property="Height" Value="12"/>
|
||||
<Setter Property="Minimum" Value="0"/>
|
||||
<Setter Property="Maximum" Value="1"/>
|
||||
<Setter Property="Margin" Value="8,0"/>
|
||||
</Style>
|
||||
<Style x:Key="CaptionButton" TargetType="Button">
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Width" Value="46"/>
|
||||
<Setter Property="Height" Value="40"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="shell:WindowChrome.IsHitTestVisibleInChrome" Value="True"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource FillHover}"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<Style x:Key="CaptionCloseButton" TargetType="Button" BasedOn="{StaticResource CaptionButton}">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource Danger}"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<Style x:Key="PlainListItem" TargetType="ListViewItem">
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="Padding" Value="6,6"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ListViewItem">
|
||||
<Border x:Name="Bd" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource FillHover}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsSelected" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource ListSelection}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="ListBox">
|
||||
<Setter Property="Background" Value="{DynamicResource Panel}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource Stroke}"/>
|
||||
</Style>
|
||||
<Style TargetType="ListView">
|
||||
<Setter Property="Background" Value="{DynamicResource Panel}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource Stroke}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
|
||||
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
|
||||
</Style>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="Padding" Value="8,4"/>
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
<Setter Property="SnapsToDevicePixels" Value="True"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ListViewItem">
|
||||
<Border x:Name="Bd"
|
||||
Background="{TemplateBinding Background}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
SnapsToDevicePixels="True">
|
||||
<Grid>
|
||||
<GridViewRowPresenter x:Name="GridRow"
|
||||
Content="{TemplateBinding Content}"
|
||||
VerticalAlignment="Center"/>
|
||||
<ContentPresenter x:Name="Content"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentStringFormat="{TemplateBinding ContentStringFormat}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="Center"
|
||||
Visibility="Collapsed"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="GridView.ColumnCollection" Value="{x:Null}">
|
||||
<Setter TargetName="Content" Property="Visibility" Value="Visible"/>
|
||||
<Setter TargetName="GridRow" Property="Visibility" Value="Collapsed"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource FillHover}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsSelected" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource ListSelection}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Foreground" Value="{DynamicResource FgMuted}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="IconListItem" TargetType="ListViewItem" BasedOn="{StaticResource {x:Type ListViewItem}}">
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Top"/>
|
||||
<Setter Property="Padding" Value="8,6"/>
|
||||
<Setter Property="Margin" Value="2"/>
|
||||
</Style>
|
||||
<Style x:Key="ColumnHeaderGripper" TargetType="Thumb">
|
||||
<Setter Property="Width" Value="8"/>
|
||||
<Setter Property="Cursor" Value="SizeWE"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Thumb">
|
||||
<Border Background="Transparent">
|
||||
<Border Width="1" HorizontalAlignment="Center" Background="{DynamicResource Stroke}"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="GridViewColumnHeader">
|
||||
<Setter Property="Foreground" Value="{DynamicResource FgMuted}"/>
|
||||
<Setter Property="Background" Value="{DynamicResource Panel}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource Stroke}"/>
|
||||
<Setter Property="BorderThickness" Value="0,0,1,1"/>
|
||||
<Setter Property="Padding" Value="8,6"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="GridViewColumnHeader">
|
||||
<Grid>
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<Thumb x:Name="PART_HeaderGripper" Style="{StaticResource ColumnHeaderGripper}"
|
||||
HorizontalAlignment="Right"/>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource FillHover}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="Role" Value="Padding">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="GridViewColumnHeader">
|
||||
<Border Background="{DynamicResource Panel}" BorderBrush="{DynamicResource Stroke}"
|
||||
BorderThickness="0,0,0,1"/>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<Style TargetType="TreeView">
|
||||
<Setter Property="Background" Value="{DynamicResource Panel}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="4,0,4,8"/>
|
||||
</Style>
|
||||
<Style TargetType="TreeViewItem">
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Padding" Value="4,3"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="SnapsToDevicePixels" Value="True"/>
|
||||
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
|
||||
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="TreeViewItem">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<ToggleButton x:Name="Expander"
|
||||
Style="{StaticResource ExpandToggle}"
|
||||
IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
ClickMode="Press"/>
|
||||
<Border x:Name="Bd"
|
||||
Grid.Column="1"
|
||||
Background="{TemplateBinding Background}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
CornerRadius="3"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter x:Name="PART_Header"
|
||||
ContentSource="Header"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
|
||||
</Border>
|
||||
<ItemsPresenter x:Name="ItemsHost" Grid.Row="1" Grid.Column="1"/>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsExpanded" Value="False">
|
||||
<Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed"/>
|
||||
</Trigger>
|
||||
<Trigger Property="HasItems" Value="False">
|
||||
<Setter TargetName="Expander" Property="Visibility" Value="Hidden"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsSelected" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource TreeSelection}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsSelected" Value="True"/>
|
||||
<Condition Property="IsSelectionActive" Value="False"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource FillHover}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
</MultiTrigger>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource FillHover}"/>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsSelected" Value="True"/>
|
||||
<Condition Property="IsMouseOver" Value="True"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource TreeSelection}"/>
|
||||
</MultiTrigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Foreground" Value="{DynamicResource FgMuted}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsPlaceholder}" Value="True">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<Style TargetType="TabControl">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="TabControl">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TabPanel Grid.Row="0" IsItemsHost="True" Margin="4,4,4,0"/>
|
||||
<Border Grid.Row="1" Background="{DynamicResource Bg}">
|
||||
<ContentPresenter x:Name="PART_SelectedContentHost"
|
||||
ContentSource="SelectedContent"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="TabItem">
|
||||
<Setter Property="Foreground" Value="{DynamicResource FgMuted}"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Padding" Value="12,8"/>
|
||||
<Setter Property="MinWidth" Value="140"/>
|
||||
<Setter Property="MinHeight" Value="32"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="TabItem">
|
||||
<Border x:Name="Bd"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
BorderBrush="Transparent"
|
||||
BorderThickness="0,0,0,2"
|
||||
Background="Transparent"
|
||||
Margin="0,0,4,0">
|
||||
<ContentPresenter ContentSource="Header" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsSelected" Value="True">
|
||||
<Setter TargetName="Bd" Property="BorderBrush" Value="{DynamicResource Accent}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource FillHover}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="ScrollBarThumb" TargetType="Thumb">
|
||||
<Setter Property="OverridesDefaultStyle" Value="True"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
<Setter Property="Focusable" Value="False"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Thumb">
|
||||
<Border x:Name="Bd"
|
||||
Background="{DynamicResource ScrollThumb}"
|
||||
CornerRadius="4"
|
||||
Margin="3,2"/>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource ScrollThumbHover}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsDragging" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource ScrollThumbPressed}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="ScrollBarThumbHorizontal" TargetType="Thumb" BasedOn="{StaticResource ScrollBarThumb}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Thumb">
|
||||
<Border x:Name="Bd"
|
||||
Background="{DynamicResource ScrollThumb}"
|
||||
CornerRadius="4"
|
||||
Margin="2,3"/>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource ScrollThumbHover}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsDragging" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource ScrollThumbPressed}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="ScrollBarPageButton" 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>
|
||||
<ControlTemplate x:Key="VerticalScrollBar" TargetType="ScrollBar">
|
||||
<Border Background="{DynamicResource ScrollTrack}" SnapsToDevicePixels="True">
|
||||
<Track x:Name="PART_Track" IsDirectionReversed="True">
|
||||
<Track.DecreaseRepeatButton>
|
||||
<RepeatButton Command="ScrollBar.PageUpCommand" Style="{StaticResource ScrollBarPageButton}"/>
|
||||
</Track.DecreaseRepeatButton>
|
||||
<Track.Thumb>
|
||||
<Thumb Style="{StaticResource ScrollBarThumb}"/>
|
||||
</Track.Thumb>
|
||||
<Track.IncreaseRepeatButton>
|
||||
<RepeatButton Command="ScrollBar.PageDownCommand" Style="{StaticResource ScrollBarPageButton}"/>
|
||||
</Track.IncreaseRepeatButton>
|
||||
</Track>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
<ControlTemplate x:Key="HorizontalScrollBar" TargetType="ScrollBar">
|
||||
<Border Background="{DynamicResource ScrollTrack}" SnapsToDevicePixels="True">
|
||||
<Track x:Name="PART_Track" IsDirectionReversed="False">
|
||||
<Track.DecreaseRepeatButton>
|
||||
<RepeatButton Command="ScrollBar.PageLeftCommand" Style="{StaticResource ScrollBarPageButton}"/>
|
||||
</Track.DecreaseRepeatButton>
|
||||
<Track.Thumb>
|
||||
<Thumb Style="{StaticResource ScrollBarThumbHorizontal}"/>
|
||||
</Track.Thumb>
|
||||
<Track.IncreaseRepeatButton>
|
||||
<RepeatButton Command="ScrollBar.PageRightCommand" Style="{StaticResource ScrollBarPageButton}"/>
|
||||
</Track.IncreaseRepeatButton>
|
||||
</Track>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
<Style TargetType="ScrollBar">
|
||||
<Setter Property="OverridesDefaultStyle" Value="True"/>
|
||||
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="False"/>
|
||||
<Setter Property="Width" Value="12"/>
|
||||
<Setter Property="MinWidth" Value="12"/>
|
||||
<Setter Property="Template" Value="{StaticResource VerticalScrollBar}"/>
|
||||
<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"/>
|
||||
<Setter Property="Template" Value="{StaticResource HorizontalScrollBar}"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<Style TargetType="ScrollViewer">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
56
src/Explorer.App/App.xaml.cs
Normal file
56
src/Explorer.App/App.xaml.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using Explorer.Presentation.ViewModels;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Serilog;
|
||||
|
||||
namespace Explorer.App;
|
||||
|
||||
public partial class App : System.Windows.Application
|
||||
{
|
||||
private IHost? _host;
|
||||
|
||||
protected override async void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
base.OnStartup(e);
|
||||
DispatcherUnhandledException += (_, args) =>
|
||||
{
|
||||
Log.Error(args.Exception, "Unhandled UI exception");
|
||||
args.Handled = true;
|
||||
};
|
||||
|
||||
var env = new Windows.WindowsAppEnvironment();
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.MinimumLevel.Information()
|
||||
.WriteTo.File(
|
||||
Path.Combine(env.LogDirectory, "explorer-.log"),
|
||||
rollingInterval: RollingInterval.Day,
|
||||
retainedFileCountLimit: 14)
|
||||
.CreateLogger();
|
||||
|
||||
_host = Host.CreateDefaultBuilder()
|
||||
.UseSerilog()
|
||||
.ConfigureServices((_, services) => services.AddExplorer())
|
||||
.Build();
|
||||
|
||||
var vm = _host.Services.GetRequiredService<MainViewModel>();
|
||||
await vm.InitializeAsync().ConfigureAwait(true);
|
||||
await _host.StartAsync().ConfigureAwait(true);
|
||||
var window = _host.Services.GetRequiredService<MainWindow>();
|
||||
window.DataContext = vm;
|
||||
window.Show();
|
||||
}
|
||||
|
||||
protected override async void OnExit(ExitEventArgs e)
|
||||
{
|
||||
if (_host is not null)
|
||||
{
|
||||
await _host.StopAsync(TimeSpan.FromSeconds(3)).ConfigureAwait(true);
|
||||
_host.Dispose();
|
||||
}
|
||||
|
||||
Log.CloseAndFlush();
|
||||
base.OnExit(e);
|
||||
}
|
||||
}
|
||||
86
src/Explorer.App/AppServices.cs
Normal file
86
src/Explorer.App/AppServices.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using Explorer.Analysis;
|
||||
using Explorer.Application;
|
||||
using Explorer.Domain;
|
||||
using Explorer.Domain.Abstractions;
|
||||
using Explorer.FileOperations;
|
||||
using Explorer.Indexing;
|
||||
using Explorer.Plugin.Abstractions;
|
||||
using Explorer.Plugin.OneDrive;
|
||||
using Explorer.Presentation.ViewModels;
|
||||
using Explorer.Search;
|
||||
using Explorer.Storage.Sqlite;
|
||||
using Explorer.Windows;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Explorer.App;
|
||||
|
||||
public static class AppServices
|
||||
{
|
||||
public static IServiceCollection AddExplorer(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IClock, SystemClock>();
|
||||
services.AddSingleton<IAppEnvironment, WindowsAppEnvironment>();
|
||||
services.AddSingleton<IVolumeService, WindowsVolumeService>();
|
||||
services.AddSingleton<IFileSystemEnumerator, WindowsFileSystemEnumerator>();
|
||||
services.AddSingleton<IUsnJournal, WindowsUsnJournal>();
|
||||
services.AddSingleton<IShellFileOperations, WindowsShellFileOperations>();
|
||||
services.AddSingleton<IOsClipboard, Services.WpfClipboard>();
|
||||
services.AddSingleton<IIndexStore>(sp =>
|
||||
{
|
||||
var env = sp.GetRequiredService<IAppEnvironment>();
|
||||
var logger = sp.GetRequiredService<ILogger<SqliteIndexStore>>();
|
||||
return new SqliteIndexStore(env.DatabasePath, logger);
|
||||
});
|
||||
services.AddSingleton<IStorageProvider, OneDriveStorageProvider>();
|
||||
services.AddSingleton<StorageProviderRegistry>();
|
||||
services.AddSingleton<IHydrationGuard, HydrationGuard>();
|
||||
services.AddSingleton<SourceManager>();
|
||||
services.AddSingleton<PathHistoryStore>();
|
||||
services.AddSingleton<CloudPlaceStore>();
|
||||
services.AddSingleton<BrowseService>();
|
||||
services.AddSingleton<FilesystemScanner>();
|
||||
services.AddSingleton<FolderReconciler>();
|
||||
services.AddSingleton<UsnChangeApplier>();
|
||||
services.AddSingleton<IndexingCoordinator>();
|
||||
services.AddSingleton<DirectoryWatcherHub>();
|
||||
services.AddSingleton<SearchService>();
|
||||
services.AddSingleton<AnalysisService>();
|
||||
services.AddSingleton<TransferQueue>();
|
||||
services.AddSingleton<FileOperationService>();
|
||||
services.AddSingleton<DuplicateHashWorker>();
|
||||
services.AddSingleton<HistoryRollupService>();
|
||||
services.AddSingleton<MainViewModel>();
|
||||
services.AddSingleton<MainWindow>();
|
||||
services.AddHostedService(sp => sp.GetRequiredService<IndexingCoordinator>());
|
||||
services.AddHostedService(sp => sp.GetRequiredService<TransferQueue>());
|
||||
services.AddHostedService(sp => sp.GetRequiredService<DuplicateHashWorker>());
|
||||
services.AddHostedService(sp => sp.GetRequiredService<HistoryRollupService>());
|
||||
services.AddHostedService<WatcherHostedService>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class WatcherHostedService : BackgroundService
|
||||
{
|
||||
private readonly DirectoryWatcherHub _hub;
|
||||
private readonly SourceManager _sources;
|
||||
|
||||
public WatcherHostedService(DirectoryWatcherHub hub, SourceManager sources)
|
||||
{
|
||||
_hub = hub;
|
||||
_sources = sources;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(20));
|
||||
await _hub.RefreshAsync(stoppingToken).ConfigureAwait(false);
|
||||
while (await timer.WaitForNextTickAsync(stoppingToken).ConfigureAwait(false))
|
||||
{
|
||||
await _sources.RefreshOnlineStateAsync(stoppingToken).ConfigureAwait(false);
|
||||
await _hub.RefreshAsync(stoppingToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/Explorer.App/AssemblyInfo.cs
Normal file
10
src/Explorer.App/AssemblyInfo.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly:ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
107
src/Explorer.App/Converters.cs
Normal file
107
src/Explorer.App/Converters.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Explorer.Domain;
|
||||
using Explorer.Presentation;
|
||||
|
||||
namespace Explorer.App;
|
||||
|
||||
public sealed class ActiveThicknessConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
=> value is true ? new Thickness(2) : new Thickness(0);
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
=> throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public sealed class FractionWidthConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var fraction = value is double d ? d : 0;
|
||||
return Math.Max(2, fraction * 360);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
=> throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public sealed class IndentConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var depth = value is double d ? d : value is int i ? i : 0;
|
||||
return new Thickness(depth, 0, 8, 0);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
=> throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public sealed class ViewModeToVisibilityConverter : IValueConverter
|
||||
{
|
||||
public FolderViewMode Match { get; set; }
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
=> value is FolderViewMode mode && mode == Match ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
=> throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public sealed class ThumbnailConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is FolderItemViewModel { IsImage: true, MayHydrateOnRead: false, FullPath: var path })
|
||||
{
|
||||
return ThumbnailCache.Load(path, decodeWidth: 240);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
=> throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public sealed class TransferActionTextConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
=> value is TransferStatus.Failed ? "Dismiss" : "Cancel";
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
=> throw new NotSupportedException();
|
||||
}
|
||||
|
||||
internal static class ThumbnailCache
|
||||
{
|
||||
public static ImageSource? Load(string path, int decodeWidth)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var bmp = new BitmapImage();
|
||||
bmp.BeginInit();
|
||||
bmp.UriSource = new Uri(path);
|
||||
bmp.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bmp.CreateOptions = BitmapCreateOptions.IgnoreImageCache | BitmapCreateOptions.IgnoreColorProfile;
|
||||
bmp.DecodePixelWidth = decodeWidth;
|
||||
bmp.EndInit();
|
||||
bmp.Freeze();
|
||||
return bmp;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/Explorer.App/Explorer.App.csproj
Normal file
38
src/Explorer.App/Explorer.App.csproj
Normal file
@@ -0,0 +1,38 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net10.0-windows</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<ApplicationIcon>..\..\explorer-workbench-icons\explorer-workbench.ico</ApplicationIcon>
|
||||
<RootNamespace>Explorer.App</RootNamespace>
|
||||
<AssemblyName>Explorer.App</AssemblyName>
|
||||
<ApplicationTitle>Explorer Workbench</ApplicationTitle>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="..\..\explorer-workbench-icons\explorer-workbench.ico" Link="Assets\explorer-workbench.ico" />
|
||||
<Resource Include="..\..\explorer-workbench-icons\explorer-workbench-20.png" Link="Assets\explorer-workbench-20.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
|
||||
<PackageReference Include="Serilog" Version="4.3.0" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Explorer.Analysis\Explorer.Analysis.csproj" />
|
||||
<ProjectReference Include="..\Explorer.Application\Explorer.Application.csproj" />
|
||||
<ProjectReference Include="..\Explorer.Domain\Explorer.Domain.csproj" />
|
||||
<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.OneDrive\Explorer.Plugin.OneDrive.csproj" />
|
||||
<ProjectReference Include="..\Explorer.Presentation\Explorer.Presentation.csproj" />
|
||||
<ProjectReference Include="..\Explorer.Search\Explorer.Search.csproj" />
|
||||
<ProjectReference Include="..\Explorer.Storage.Sqlite\Explorer.Storage.Sqlite.csproj" />
|
||||
<ProjectReference Include="..\Explorer.Windows\Explorer.Windows.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
65
src/Explorer.App/ListViewLayout.cs
Normal file
65
src/Explorer.App/ListViewLayout.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Explorer.App;
|
||||
|
||||
/// <summary>
|
||||
/// Makes the first GridView column consume leftover width so Details/List fill the pane.
|
||||
/// </summary>
|
||||
public static class ListViewLayout
|
||||
{
|
||||
public static readonly DependencyProperty StretchFirstColumnProperty =
|
||||
DependencyProperty.RegisterAttached(
|
||||
"StretchFirstColumn",
|
||||
typeof(bool),
|
||||
typeof(ListViewLayout),
|
||||
new PropertyMetadata(false, OnStretchChanged));
|
||||
|
||||
public static void SetStretchFirstColumn(ListView element, bool value) => element.SetValue(StretchFirstColumnProperty, value);
|
||||
|
||||
public static bool GetStretchFirstColumn(ListView element) => (bool)element.GetValue(StretchFirstColumnProperty);
|
||||
|
||||
private static void OnStretchChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is not ListView list)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.NewValue is true)
|
||||
{
|
||||
list.SizeChanged += OnListSizeChanged;
|
||||
list.Loaded += OnListSizeChanged;
|
||||
}
|
||||
else
|
||||
{
|
||||
list.SizeChanged -= OnListSizeChanged;
|
||||
list.Loaded -= OnListSizeChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnListSizeChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is ListView list)
|
||||
{
|
||||
Stretch(list);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Stretch(ListView list)
|
||||
{
|
||||
if (list.View is not GridView view || view.Columns.Count == 0 || list.ActualWidth <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double reserved = 8 + SystemParameters.VerticalScrollBarWidth;
|
||||
for (var i = 1; i < view.Columns.Count; i++)
|
||||
{
|
||||
var width = view.Columns[i].Width;
|
||||
reserved += double.IsNaN(width) ? 120 : width;
|
||||
}
|
||||
|
||||
view.Columns[0].Width = Math.Max(96, list.ActualWidth - reserved);
|
||||
}
|
||||
}
|
||||
630
src/Explorer.App/MainWindow.xaml
Normal file
630
src/Explorer.App/MainWindow.xaml
Normal file
@@ -0,0 +1,630 @@
|
||||
<Window x:Class="Explorer.App.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="clr-namespace:Explorer.Presentation.ViewModels;assembly=Explorer.Presentation"
|
||||
xmlns:local="clr-namespace:Explorer.App"
|
||||
xmlns:shell="clr-namespace:System.Windows.Shell;assembly=PresentationFramework"
|
||||
Title="Explorer Workbench"
|
||||
Icon="pack://application:,,,/Assets/explorer-workbench.ico"
|
||||
Height="820" Width="1280"
|
||||
MinHeight="500" MinWidth="800"
|
||||
Background="{DynamicResource Bg}"
|
||||
Foreground="{DynamicResource Fg}"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
WindowStyle="None"
|
||||
ResizeMode="CanResize"
|
||||
UseLayoutRounding="True"
|
||||
SnapsToDevicePixels="True"
|
||||
PreviewKeyDown="OnPreviewKeyDown">
|
||||
<shell:WindowChrome.WindowChrome>
|
||||
<shell:WindowChrome CaptionHeight="40"
|
||||
ResizeBorderThickness="6"
|
||||
GlassFrameThickness="0"
|
||||
CornerRadius="0"
|
||||
UseAeroCaptionButtons="False"/>
|
||||
</shell:WindowChrome.WindowChrome>
|
||||
<DockPanel>
|
||||
<Border DockPanel.Dock="Top" Height="40" Background="{DynamicResource Panel}"
|
||||
BorderBrush="{DynamicResource Stroke}" BorderThickness="0,0,0,1"
|
||||
MouseLeftButtonDown="OnTitleBarMouseDown">
|
||||
<Grid>
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="12,0,0,0" IsHitTestVisible="False">
|
||||
<Image Width="20" Height="20" Margin="0,0,8,0" VerticalAlignment="Center"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Source="pack://application:,,,/Assets/explorer-workbench-20.png"/>
|
||||
<TextBlock Text="Explorer Workbench" FontWeight="SemiBold" VerticalAlignment="Center" Foreground="{DynamicResource Fg}"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button Style="{StaticResource CaptionButton}" Content="─" Click="OnMinimize" ToolTip="Minimize"/>
|
||||
<Button x:Name="MaxRestoreButton" Style="{StaticResource CaptionButton}" Content="☐" Click="OnMaxRestore" ToolTip="Maximize"/>
|
||||
<Button Style="{StaticResource CaptionCloseButton}" Content="✕" Click="OnCloseWindow" ToolTip="Close"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border DockPanel.Dock="Top" Background="{DynamicResource Panel}" BorderBrush="{DynamicResource Stroke}" BorderThickness="0,0,0,1" Padding="8">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="280"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button Content="←" Command="{Binding BackCommand}" Width="36" Margin="0,0,4,0" ToolTip="Back (Alt+Left)"/>
|
||||
<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"/>
|
||||
</StackPanel>
|
||||
<ComboBox Grid.Column="1" Margin="12,0" Style="{StaticResource PathComboBox}"
|
||||
ItemsSource="{Binding PathHistory}"
|
||||
Text="{Binding PathText, UpdateSourceTrigger=PropertyChanged}"
|
||||
PreviewKeyDown="OnPathKeyDown"
|
||||
SelectionChanged="OnPathHistorySelected"/>
|
||||
<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}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Border DockPanel.Dock="Bottom" Background="{DynamicResource Panel}" BorderBrush="{DynamicResource Stroke}" BorderThickness="0,1,0,0" Padding="8,6">
|
||||
<Grid>
|
||||
<TextBlock Text="{Binding Footer}" Foreground="{DynamicResource FgMuted}" VerticalAlignment="Center"/>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<ItemsControl ItemsSource="{Binding Transfers.Jobs}" Margin="0,0,12,0">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,10,0">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="11" Foreground="{DynamicResource FgMuted}"
|
||||
Text="{Binding Op, StringFormat={}{0}:}"/>
|
||||
<TextBlock VerticalAlignment="Center" FontSize="11" Margin="4,0,0,0" Text="{Binding Status}"/>
|
||||
<Button Margin="6,0,0,0" Padding="6,2" FontSize="11"
|
||||
Content="{Binding Status, Converter={StaticResource TransferAction}}"
|
||||
Command="{Binding DataContext.CancelTransferCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
CommandParameter="{Binding}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<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"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="260" MinWidth="160"/>
|
||||
<ColumnDefinition Width="6"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<DockPanel Grid.Column="0" Background="{DynamicResource Panel}">
|
||||
<TextBlock DockPanel.Dock="Top" Margin="12,10,12,6" FontWeight="SemiBold" Foreground="{DynamicResource Fg}" Text="Locations"/>
|
||||
<TreeView x:Name="NavTree" ItemsSource="{Binding Tree.Roots}"
|
||||
SelectedItemChanged="OnTreeSelected"
|
||||
TreeViewItem.Expanded="OnTreeExpanded"
|
||||
HorizontalContentAlignment="Stretch">
|
||||
<TreeView.ItemTemplate>
|
||||
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock FontFamily="{StaticResource Symbol}" Text="{Binding Glyph}" Margin="0,0,8,0" Foreground="{DynamicResource Accent}"/>
|
||||
<TextBlock Text="{Binding Label}" Foreground="{DynamicResource Fg}"/>
|
||||
<TextBlock Text="{Binding Status}" Margin="8,0,0,0" Foreground="{DynamicResource FgMuted}" FontSize="11"/>
|
||||
</StackPanel>
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.ItemTemplate>
|
||||
</TreeView>
|
||||
</DockPanel>
|
||||
<GridSplitter Grid.Column="1" Width="6" HorizontalAlignment="Stretch" Background="{DynamicResource Stroke}"/>
|
||||
<DockPanel Grid.Column="2" LastChildFill="True">
|
||||
<TabControl x:Name="Tabs" ItemsSource="{Binding Tabs}" SelectedItem="{Binding ActiveTab}"
|
||||
SelectionChanged="OnTabChanged"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<TabControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" MinWidth="120">
|
||||
<TextBlock Text="⋮" Margin="0,0,8,0" Foreground="{DynamicResource FgMuted}" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding Title}" Margin="0,0,8,0" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" MaxWidth="160"/>
|
||||
<Button Content="×" Padding="4,0" MinWidth="22" Click="OnCloseTab" Tag="{Binding}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</TabControl.ItemTemplate>
|
||||
<TabControl.ContentTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Loaded="OnPaneSplitLoaded"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" MinWidth="0"/>
|
||||
<ColumnDefinition Width="0" MinWidth="0"/>
|
||||
<ColumnDefinition Width="0" MinWidth="0"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border Grid.Column="0" DataContext="{Binding Left}" Style="{StaticResource PaneChrome}"
|
||||
PreviewMouseDown="OnPaneChromeMouseDown">
|
||||
<DockPanel LastChildFill="True">
|
||||
<ContentControl DockPanel.Dock="Top" Content="{Binding}" ContentTemplate="{StaticResource PaneAddressBar}"
|
||||
Focusable="False"/>
|
||||
<Border DockPanel.Dock="Top" Background="{DynamicResource Banner}" Padding="10,8"
|
||||
Visibility="{Binding ShowIndexBanner, Converter={StaticResource BoolVis}}">
|
||||
<DockPanel>
|
||||
<Button DockPanel.Dock="Right" Content="Build index" Click="OnBuildIndex"/>
|
||||
<TextBlock Text="{Binding IndexBannerText}" TextWrapping="Wrap" VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource Fg}"/>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
<Grid>
|
||||
<ListView ItemsSource="{Binding Items}"
|
||||
MouseDoubleClick="OnItemDoubleClick"
|
||||
SelectionChanged="OnSelectionChanged"
|
||||
AllowDrop="True"
|
||||
PreviewMouseLeftButtonDown="OnListMouseDown"
|
||||
Drop="OnListDrop"
|
||||
DragOver="OnListDragOver"
|
||||
VirtualizingPanel.IsVirtualizing="True"
|
||||
VirtualizingPanel.VirtualizationMode="Recycling"
|
||||
GotFocus="OnPaneFocus"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
local:ListViewLayout.StretchFirstColumn="True"
|
||||
Visibility="{Binding ViewMode, Converter={StaticResource ViewDetails}}">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridViewColumn Header="Name" Width="120" CellTemplate="{StaticResource NameWithIcon}"/>
|
||||
<GridViewColumn Header="Date modified" Width="148" DisplayMemberBinding="{Binding ModifiedLabel}"/>
|
||||
<GridViewColumn Header="Type" Width="100" DisplayMemberBinding="{Binding TypeLabel}"/>
|
||||
<GridViewColumn Header="Size" Width="110" DisplayMemberBinding="{Binding SizeLabel}"/>
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
<ListView.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="Open" Click="OnCtxOpen"/>
|
||||
<Separator/>
|
||||
<MenuItem Header="Cut" Command="{Binding DataContext.CutCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<MenuItem Header="Copy" Command="{Binding DataContext.CopyCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<MenuItem Header="Paste" Command="{Binding DataContext.PasteCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<MenuItem Header="Delete" Command="{Binding DataContext.DeleteCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<MenuItem Header="Rename" Click="OnCtxRename"/>
|
||||
<Separator/>
|
||||
<MenuItem Header="New folder" Command="{Binding DataContext.NewFolderCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<MenuItem Header="Copy path" Command="{Binding DataContext.CopyPathCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<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.ShowCloudPin, RelativeSource={RelativeSource AncestorType=Window}, Converter={StaticResource BoolVis}}"/>
|
||||
<MenuItem Header="Always keep on this device"
|
||||
Command="{Binding DataContext.PinCloudCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
Visibility="{Binding DataContext.ShowCloudPin, RelativeSource={RelativeSource AncestorType=Window}, Converter={StaticResource BoolVis}}"/>
|
||||
<MenuItem Header="Free up space"
|
||||
Command="{Binding DataContext.FreeUpCloudSpaceCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
Visibility="{Binding DataContext.ShowCloudDehydrate, RelativeSource={RelativeSource AncestorType=Window}, Converter={StaticResource BoolVis}}"/>
|
||||
</ContextMenu>
|
||||
</ListView.ContextMenu>
|
||||
</ListView>
|
||||
<ListView ItemsSource="{Binding Items}"
|
||||
MouseDoubleClick="OnItemDoubleClick"
|
||||
SelectionChanged="OnSelectionChanged"
|
||||
AllowDrop="True"
|
||||
PreviewMouseLeftButtonDown="OnListMouseDown"
|
||||
Drop="OnListDrop"
|
||||
DragOver="OnListDragOver"
|
||||
VirtualizingPanel.IsVirtualizing="True"
|
||||
VirtualizingPanel.VirtualizationMode="Recycling"
|
||||
GotFocus="OnPaneFocus"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
local:ListViewLayout.StretchFirstColumn="True"
|
||||
Visibility="{Binding ViewMode, Converter={StaticResource ViewList}}">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridViewColumn Header="Name" Width="120" CellTemplate="{StaticResource NameWithIcon}"/>
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
<ListView ItemsSource="{Binding Items}"
|
||||
ItemTemplate="{StaticResource PreviewTile}"
|
||||
ItemContainerStyle="{StaticResource IconListItem}"
|
||||
MouseDoubleClick="OnItemDoubleClick"
|
||||
SelectionChanged="OnSelectionChanged"
|
||||
AllowDrop="True"
|
||||
PreviewMouseLeftButtonDown="OnListMouseDown"
|
||||
Drop="OnListDrop"
|
||||
DragOver="OnListDragOver"
|
||||
GotFocus="OnPaneFocus"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||
HorizontalContentAlignment="Left"
|
||||
Visibility="{Binding ViewMode, Converter={StaticResource ViewPreview}}">
|
||||
<ListView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=ScrollViewer}}"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ListView.ItemsPanel>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
<GridSplitter Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
ResizeBehavior="PreviousAndNext"
|
||||
Background="{DynamicResource Stroke}"
|
||||
Visibility="{Binding IsSplit, Converter={StaticResource BoolVis}}"
|
||||
DragCompleted="OnSplitDragCompleted"/>
|
||||
<Border Grid.Column="2"
|
||||
DataContext="{Binding Right}"
|
||||
Style="{StaticResource PaneChrome}"
|
||||
PreviewMouseDown="OnPaneChromeMouseDown"
|
||||
Visibility="{Binding DataContext.IsSplit, RelativeSource={RelativeSource AncestorType=Grid}, Converter={StaticResource BoolVis}}">
|
||||
<DockPanel LastChildFill="True">
|
||||
<ContentControl DockPanel.Dock="Top" Content="{Binding}" ContentTemplate="{StaticResource PaneAddressBar}"
|
||||
Focusable="False"/>
|
||||
<Border DockPanel.Dock="Top" Background="{DynamicResource Banner}" Padding="10,8"
|
||||
Visibility="{Binding ShowIndexBanner, Converter={StaticResource BoolVis}}">
|
||||
<DockPanel>
|
||||
<Button DockPanel.Dock="Right" Content="Build index" Click="OnBuildIndex"/>
|
||||
<TextBlock Text="{Binding IndexBannerText}" TextWrapping="Wrap" VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource Fg}"/>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
<Grid>
|
||||
<ListView ItemsSource="{Binding Items}"
|
||||
MouseDoubleClick="OnItemDoubleClick"
|
||||
SelectionChanged="OnSelectionChanged"
|
||||
AllowDrop="True"
|
||||
PreviewMouseLeftButtonDown="OnListMouseDown"
|
||||
Drop="OnListDrop"
|
||||
DragOver="OnListDragOver"
|
||||
VirtualizingPanel.IsVirtualizing="True"
|
||||
VirtualizingPanel.VirtualizationMode="Recycling"
|
||||
GotFocus="OnPaneFocus"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
local:ListViewLayout.StretchFirstColumn="True"
|
||||
Visibility="{Binding ViewMode, Converter={StaticResource ViewDetails}}">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridViewColumn Header="Name" Width="120" CellTemplate="{StaticResource NameWithIcon}"/>
|
||||
<GridViewColumn Header="Date modified" Width="148" DisplayMemberBinding="{Binding ModifiedLabel}"/>
|
||||
<GridViewColumn Header="Type" Width="100" DisplayMemberBinding="{Binding TypeLabel}"/>
|
||||
<GridViewColumn Header="Size" Width="110" DisplayMemberBinding="{Binding SizeLabel}"/>
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
<ListView ItemsSource="{Binding Items}"
|
||||
MouseDoubleClick="OnItemDoubleClick"
|
||||
SelectionChanged="OnSelectionChanged"
|
||||
AllowDrop="True"
|
||||
PreviewMouseLeftButtonDown="OnListMouseDown"
|
||||
Drop="OnListDrop"
|
||||
DragOver="OnListDragOver"
|
||||
VirtualizingPanel.IsVirtualizing="True"
|
||||
VirtualizingPanel.VirtualizationMode="Recycling"
|
||||
GotFocus="OnPaneFocus"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
local:ListViewLayout.StretchFirstColumn="True"
|
||||
Visibility="{Binding ViewMode, Converter={StaticResource ViewList}}">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridViewColumn Header="Name" Width="120" CellTemplate="{StaticResource NameWithIcon}"/>
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
<ListView ItemsSource="{Binding Items}"
|
||||
ItemTemplate="{StaticResource PreviewTile}"
|
||||
ItemContainerStyle="{StaticResource IconListItem}"
|
||||
MouseDoubleClick="OnItemDoubleClick"
|
||||
SelectionChanged="OnSelectionChanged"
|
||||
AllowDrop="True"
|
||||
PreviewMouseLeftButtonDown="OnListMouseDown"
|
||||
Drop="OnListDrop"
|
||||
DragOver="OnListDragOver"
|
||||
GotFocus="OnPaneFocus"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||
HorizontalContentAlignment="Left"
|
||||
Visibility="{Binding ViewMode, Converter={StaticResource ViewPreview}}">
|
||||
<ListView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=ScrollViewer}}"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ListView.ItemsPanel>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</TabControl.ContentTemplate>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
|
||||
<Border Grid.Column="2" Background="#99000000" Visibility="{Binding Search.IsOpen, Converter={StaticResource BoolVis}}">
|
||||
<Border Background="{DynamicResource Panel}" BorderBrush="{DynamicResource Stroke}" BorderThickness="1" Margin="40" Padding="16">
|
||||
<DockPanel>
|
||||
<DockPanel DockPanel.Dock="Top" LastChildFill="True" Margin="0,0,0,12">
|
||||
<Button DockPanel.Dock="Right" Content="Close" Click="OnCloseSearch" Margin="8,0,0,0"/>
|
||||
<TextBlock Text="Search" FontSize="18" FontWeight="SemiBold" Foreground="{DynamicResource Fg}" VerticalAlignment="Center"/>
|
||||
</DockPanel>
|
||||
<Grid DockPanel.Dock="Top" Margin="0,0,0,10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="220"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Text="Name" Foreground="{DynamicResource FgMuted}" VerticalAlignment="Center" Margin="0,0,8,8"/>
|
||||
<TextBox Grid.Column="1" Margin="0,0,16,8" Text="{Binding Search.Text, UpdateSourceTrigger=PropertyChanged}"
|
||||
KeyDown="OnSearchKeyDown"/>
|
||||
<TextBlock Grid.Column="2" Text="Scope" Foreground="{DynamicResource FgMuted}" VerticalAlignment="Center" Margin="0,0,8,8"/>
|
||||
<ComboBox Grid.Column="3" Margin="0,0,0,8"
|
||||
ItemsSource="{Binding Search.ScopeChoices}"
|
||||
DisplayMemberPath="Label"
|
||||
SelectedValuePath="Kind"
|
||||
SelectedValue="{Binding Search.Scope}"/>
|
||||
<TextBlock Grid.Row="1" Text="Extension" Foreground="{DynamicResource FgMuted}" VerticalAlignment="Center" Margin="0,0,8,0"/>
|
||||
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal" Margin="0,0,16,0">
|
||||
<TextBox Width="90" Text="{Binding Search.Extension, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
<CheckBox Content="Files" Margin="16,0,8,0" VerticalAlignment="Center" Foreground="{DynamicResource Fg}"
|
||||
IsChecked="{Binding Search.FilesOnly}"/>
|
||||
<CheckBox Content="Folders" VerticalAlignment="Center" Foreground="{DynamicResource Fg}"
|
||||
IsChecked="{Binding Search.FoldersOnly}"/>
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Row="1" Grid.Column="2" Text="Size" Foreground="{DynamicResource FgMuted}" VerticalAlignment="Center" Margin="0,0,8,0"/>
|
||||
<StackPanel Grid.Row="1" Grid.Column="3" Orientation="Horizontal">
|
||||
<TextBox Width="90" Margin="0,0,8,0" Text="{Binding Search.MinSizeText, UpdateSourceTrigger=PropertyChanged}"
|
||||
ToolTip="Minimum size, e.g. 10MB"/>
|
||||
<TextBox Width="90" Text="{Binding Search.MaxSizeText, UpdateSourceTrigger=PropertyChanged}"
|
||||
ToolTip="Maximum size, e.g. 1GB"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<DockPanel DockPanel.Dock="Top" Margin="0,0,0,8">
|
||||
<Button DockPanel.Dock="Right" Content="Search" Command="{Binding SearchCommand}"/>
|
||||
<TextBlock Text="{Binding Search.Status}" Foreground="{DynamicResource Fg}" VerticalAlignment="Center"/>
|
||||
</DockPanel>
|
||||
<ProgressBar DockPanel.Dock="Top" Margin="0,0,0,8" IsIndeterminate="True"
|
||||
Visibility="{Binding Search.IsBusy, Converter={StaticResource BoolVis}}"/>
|
||||
<ListView ItemsSource="{Binding Search.Results}" MouseDoubleClick="OnSearchDoubleClick"
|
||||
VirtualizingPanel.IsVirtualizing="True"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||
Background="{DynamicResource Panel}" Foreground="{DynamicResource Fg}">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridViewColumn Header="Name" Width="220" CellTemplate="{StaticResource NameWithIcon}"/>
|
||||
<GridViewColumn Header="Path" Width="420" DisplayMemberBinding="{Binding FullPath}"/>
|
||||
<GridViewColumn Header="Size" Width="100" DisplayMemberBinding="{Binding SizeLabel}"/>
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
<Border Grid.Column="2" Background="#99000000" Visibility="{Binding Analysis.IsOpen, Converter={StaticResource BoolVis}}">
|
||||
<Border Background="{DynamicResource Panel}" BorderBrush="{DynamicResource Stroke}" BorderThickness="1" Margin="24" Padding="16">
|
||||
<DockPanel>
|
||||
<DockPanel DockPanel.Dock="Top" Margin="0,0,0,12">
|
||||
<Button DockPanel.Dock="Right" Content="Close" Command="{Binding Analysis.CloseCommand}"/>
|
||||
<TextBlock Text="Storage" FontSize="18" FontWeight="SemiBold" Foreground="{DynamicResource Fg}" VerticalAlignment="Center"/>
|
||||
</DockPanel>
|
||||
<DockPanel DockPanel.Dock="Top" Margin="0,0,0,8">
|
||||
<TextBlock DockPanel.Dock="Left" Text="Location" Foreground="{DynamicResource FgMuted}" VerticalAlignment="Center" Margin="0,0,8,0"/>
|
||||
<ComboBox DockPanel.Dock="Left" ItemsSource="{Binding Analysis.Scopes}" SelectedItem="{Binding Analysis.SelectedScope}"
|
||||
DisplayMemberPath="Label" Width="220" Margin="0,0,16,0"/>
|
||||
<TextBlock DockPanel.Dock="Left" Text="View" Foreground="{DynamicResource FgMuted}" VerticalAlignment="Center" Margin="0,0,8,0"/>
|
||||
<ComboBox DockPanel.Dock="Left" ItemsSource="{Binding Analysis.Pages}" SelectedItem="{Binding Analysis.Page}" Width="150" Margin="0,0,16,0"/>
|
||||
<TextBlock Text="{Binding Analysis.Status}" Foreground="{DynamicResource FgMuted}" VerticalAlignment="Center"/>
|
||||
</DockPanel>
|
||||
<WrapPanel DockPanel.Dock="Top" Margin="0,0,0,10">
|
||||
<Button Content="Open" Command="{Binding OpenStorageHereCommand}" Margin="0,0,6,0" IsEnabled="{Binding Analysis.CanAct}"/>
|
||||
<Button Content="Open in other pane" Command="{Binding OpenStorageOtherCommand}" Margin="0,0,6,0"
|
||||
IsEnabled="{Binding Analysis.CanAct}"
|
||||
Visibility="{Binding ActiveTab.IsSplit, Converter={StaticResource BoolVis}}"/>
|
||||
<Button Content="Open in new tab" Command="{Binding OpenStorageTabCommand}" Margin="0,0,6,0" IsEnabled="{Binding Analysis.CanAct}"/>
|
||||
<Button Content="Search here" Command="{Binding SearchStorageCommand}" Margin="0,0,6,0" IsEnabled="{Binding Analysis.CanAct}"/>
|
||||
<Button Content="Rescan" Command="{Binding RescanStorageCommand}" Margin="0,0,6,0" IsEnabled="{Binding Analysis.CanAct}"/>
|
||||
<Button Content="Copy path" Command="{Binding CopyStoragePathCommand}" IsEnabled="{Binding Analysis.CanAct}"/>
|
||||
</WrapPanel>
|
||||
<ProgressBar DockPanel.Dock="Top" Margin="0,0,0,8" Height="4" IsIndeterminate="True"
|
||||
IsHitTestVisible="False"
|
||||
Visibility="{Binding Analysis.IsBusy, Converter={StaticResource BoolVis}}"/>
|
||||
<Grid>
|
||||
<Grid Visibility="{Binding Analysis.IsTree, Converter={StaticResource BoolVis}}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Margin="6,0,22,6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="170"/>
|
||||
<ColumnDefinition Width="220"/>
|
||||
<ColumnDefinition Width="88"/>
|
||||
<ColumnDefinition Width="72"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="Name" Foreground="{DynamicResource FgMuted}"/>
|
||||
<TextBlock Grid.Column="1" Text="Contents" Foreground="{DynamicResource FgMuted}"/>
|
||||
<TextBlock Grid.Column="2" Text="Relative size" Foreground="{DynamicResource FgMuted}" Margin="8,0"/>
|
||||
<TextBlock Grid.Column="3" Text="Size" HorizontalAlignment="Right" Foreground="{DynamicResource FgMuted}"/>
|
||||
<TextBlock Grid.Column="4" Text="State" Margin="8,0,0,0" Foreground="{DynamicResource FgMuted}"/>
|
||||
</Grid>
|
||||
<ListView Grid.Row="1" ItemsSource="{Binding Analysis.VisibleNodes}"
|
||||
SelectedItem="{Binding Analysis.SelectedNode}"
|
||||
MouseDoubleClick="OnStorageTreeDoubleClick"
|
||||
ItemContainerStyle="{StaticResource PlainListItem}"
|
||||
Background="{DynamicResource Panel}" Foreground="{DynamicResource Fg}"
|
||||
VirtualizingPanel.IsVirtualizing="True"
|
||||
VirtualizingPanel.VirtualizationMode="Recycling"
|
||||
ScrollViewer.CanContentScroll="True">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Margin="0,2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="170"/>
|
||||
<ColumnDefinition Width="220"/>
|
||||
<ColumnDefinition Width="88"/>
|
||||
<ColumnDefinition Width="72"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Orientation="Horizontal" Margin="{Binding Indent, Converter={StaticResource Indent}}">
|
||||
<Grid Width="18" Height="18" Margin="0,0,6,0">
|
||||
<Button Width="18" Height="18" Padding="0"
|
||||
Command="{Binding DataContext.Analysis.ToggleNodeCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
CommandParameter="{Binding}"
|
||||
Visibility="{Binding CanExpand, Converter={StaticResource BoolVis}}">
|
||||
<TextBlock FontSize="11" Foreground="{DynamicResource FgMuted}">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Text" Value="▸"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsExpanded}" Value="True">
|
||||
<Setter Property="Text" Value="▾"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</Button>
|
||||
</Grid>
|
||||
<TextBlock FontFamily="{StaticResource Symbol}" Text="{Binding Glyph}" FontSize="14"
|
||||
Foreground="{DynamicResource Accent}" Margin="0,0,8,0" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding Name}" Foreground="{DynamicResource Fg}" TextTrimming="CharacterEllipsis"
|
||||
VerticalAlignment="Center" ToolTip="{Binding FullPath}"/>
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Column="1" Text="{Binding CountLabel}" Foreground="{DynamicResource FgMuted}" VerticalAlignment="Center"/>
|
||||
<ProgressBar Grid.Column="2" Style="{StaticResource StorageSizeBar}" Value="{Binding Fraction}"
|
||||
ToolTip="{Binding SizeLabel}"/>
|
||||
<TextBlock Grid.Column="3" Text="{Binding SizeLabel}" HorizontalAlignment="Right" Foreground="{DynamicResource Fg}" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Column="4" Text="{Binding State}" Margin="8,0,0,0" Foreground="{DynamicResource FgMuted}" VerticalAlignment="Center"
|
||||
Visibility="{Binding HasState, Converter={StaticResource BoolVis}}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
<ListView.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="Open" Command="{Binding DataContext.OpenStorageHereCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<MenuItem Header="Open in other pane" Command="{Binding DataContext.OpenStorageOtherCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
Visibility="{Binding DataContext.ActiveTab.IsSplit, RelativeSource={RelativeSource AncestorType=Window}, Converter={StaticResource BoolVis}}"/>
|
||||
<MenuItem Header="Open in new tab" Command="{Binding DataContext.OpenStorageTabCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<MenuItem Header="Search within this location" Command="{Binding DataContext.SearchStorageCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<MenuItem Header="Rescan this location" Command="{Binding DataContext.RescanStorageCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<MenuItem Header="Copy path" Command="{Binding DataContext.CopyStoragePathCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
</ContextMenu>
|
||||
</ListView.ContextMenu>
|
||||
</ListView>
|
||||
</Grid>
|
||||
<Grid Visibility="{Binding Analysis.IsRanking, Converter={StaticResource BoolVis}}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Margin="6,0,22,6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="180"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="140"/>
|
||||
<ColumnDefinition Width="200"/>
|
||||
<ColumnDefinition Width="88"/>
|
||||
<ColumnDefinition Width="72"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="Name" Foreground="{DynamicResource FgMuted}"/>
|
||||
<TextBlock Grid.Column="1" Text="Location" Foreground="{DynamicResource FgMuted}" Margin="8,0"/>
|
||||
<TextBlock Grid.Column="2" Text="Contents" Foreground="{DynamicResource FgMuted}"/>
|
||||
<TextBlock Grid.Column="3" Text="Relative size" Foreground="{DynamicResource FgMuted}" Margin="8,0"/>
|
||||
<TextBlock Grid.Column="4" Text="Size" HorizontalAlignment="Right" Foreground="{DynamicResource FgMuted}"/>
|
||||
<TextBlock Grid.Column="5" Text="State" Margin="8,0,0,0" Foreground="{DynamicResource FgMuted}"/>
|
||||
</Grid>
|
||||
<ListView Grid.Row="1" ItemsSource="{Binding Analysis.Rows}"
|
||||
SelectedItem="{Binding Analysis.SelectedRow}"
|
||||
MouseDoubleClick="OnStorageRankDoubleClick"
|
||||
ItemContainerStyle="{StaticResource PlainListItem}"
|
||||
Background="{DynamicResource Panel}" Foreground="{DynamicResource Fg}"
|
||||
VirtualizingPanel.IsVirtualizing="True"
|
||||
VirtualizingPanel.VirtualizationMode="Recycling"
|
||||
ScrollViewer.CanContentScroll="True">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Margin="0,2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="180"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="140"/>
|
||||
<ColumnDefinition Width="200"/>
|
||||
<ColumnDefinition Width="88"/>
|
||||
<ColumnDefinition Width="72"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding Name}" Foreground="{DynamicResource Fg}" TextTrimming="CharacterEllipsis"
|
||||
VerticalAlignment="Center" ToolTip="{Binding Path}"/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding ShortPath}" Foreground="{DynamicResource FgMuted}"
|
||||
TextTrimming="CharacterEllipsis" VerticalAlignment="Center" Margin="8,0" ToolTip="{Binding Path}"/>
|
||||
<TextBlock Grid.Column="2" Text="{Binding CountLabel}" Foreground="{DynamicResource FgMuted}" VerticalAlignment="Center"/>
|
||||
<ProgressBar Grid.Column="3" Style="{StaticResource StorageSizeBar}" Value="{Binding Fraction}"
|
||||
ToolTip="{Binding SizeLabel}"/>
|
||||
<TextBlock Grid.Column="4" Text="{Binding SizeLabel}" HorizontalAlignment="Right" Foreground="{DynamicResource Fg}" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Column="5" Text="{Binding State}" Margin="8,0,0,0" Foreground="{DynamicResource FgMuted}" VerticalAlignment="Center"
|
||||
Visibility="{Binding HasState, Converter={StaticResource BoolVis}}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
<ListView.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="Open" Command="{Binding DataContext.OpenStorageHereCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<MenuItem Header="Open in other pane" Command="{Binding DataContext.OpenStorageOtherCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
Visibility="{Binding DataContext.ActiveTab.IsSplit, RelativeSource={RelativeSource AncestorType=Window}, Converter={StaticResource BoolVis}}"/>
|
||||
<MenuItem Header="Open in new tab" Command="{Binding DataContext.OpenStorageTabCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<MenuItem Header="Search within this location" Command="{Binding DataContext.SearchStorageCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<MenuItem Header="Rescan this location" Command="{Binding DataContext.RescanStorageCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<MenuItem Header="Copy path" Command="{Binding DataContext.CopyStoragePathCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
</ContextMenu>
|
||||
</ListView.ContextMenu>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
<Border Grid.Column="2" Background="#99000000" Visibility="{Binding Duplicates.IsOpen, Converter={StaticResource BoolVis}}">
|
||||
<Border Background="{DynamicResource Panel}" BorderBrush="{DynamicResource Stroke}" BorderThickness="1" Margin="80" Padding="16">
|
||||
<DockPanel>
|
||||
<DockPanel DockPanel.Dock="Top" Margin="0,0,0,8">
|
||||
<Button DockPanel.Dock="Right" Content="Close" Click="OnCloseDuplicates"/>
|
||||
<TextBlock Text="Duplicates" FontSize="18" FontWeight="SemiBold" Foreground="{DynamicResource Fg}"/>
|
||||
</DockPanel>
|
||||
<TextBlock DockPanel.Dock="Top" Text="{Binding Duplicates.Status}" Foreground="{DynamicResource Fg}" Margin="0,0,0,8"/>
|
||||
<ProgressBar DockPanel.Dock="Top" Margin="0,0,0,8" IsIndeterminate="True"
|
||||
Visibility="{Binding Duplicates.IsBusy, Converter={StaticResource BoolVis}}"/>
|
||||
<ListBox ItemsSource="{Binding Duplicates.Groups}" Background="{DynamicResource Panel}" Foreground="{DynamicResource Fg}"
|
||||
BorderBrush="{DynamicResource Stroke}">
|
||||
<ListBox.ItemContainerStyle>
|
||||
<Style TargetType="ListBoxItem">
|
||||
<Setter Property="Foreground" Value="{DynamicResource Fg}"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Padding" Value="8,6"/>
|
||||
</Style>
|
||||
</ListBox.ItemContainerStyle>
|
||||
</ListBox>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</Border>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
525
src/Explorer.App/MainWindow.xaml.cs
Normal file
525
src/Explorer.App/MainWindow.xaml.cs
Normal file
@@ -0,0 +1,525 @@
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
using Explorer.Domain;
|
||||
using Explorer.Presentation;
|
||||
using Explorer.Presentation.ViewModels;
|
||||
|
||||
namespace Explorer.App;
|
||||
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private Point _dragStart;
|
||||
private bool _dragPending;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContextChanged += (_, _) =>
|
||||
{
|
||||
if (DataContext is MainViewModel vm)
|
||||
{
|
||||
vm.PropertyChanged += (_, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(MainViewModel.Theme))
|
||||
{
|
||||
ApplyTheme(vm.Theme);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private MainViewModel Vm => (MainViewModel)DataContext;
|
||||
|
||||
private void ApplyTheme(string theme)
|
||||
{
|
||||
var dicts = System.Windows.Application.Current.Resources.MergedDictionaries;
|
||||
dicts.Clear();
|
||||
var uri = theme == "Light"
|
||||
? new Uri("Themes/Light.xaml", UriKind.Relative)
|
||||
: new Uri("Themes/Dark.xaml", UriKind.Relative);
|
||||
dicts.Add(new ResourceDictionary { Source = uri });
|
||||
}
|
||||
|
||||
private void OnTitleBarMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (e.ChangedButton != MouseButton.Left)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.ClickCount == 2)
|
||||
{
|
||||
ToggleMaximized();
|
||||
return;
|
||||
}
|
||||
|
||||
DragMove();
|
||||
}
|
||||
|
||||
private void OnMinimize(object sender, RoutedEventArgs e) => WindowState = WindowState.Minimized;
|
||||
|
||||
private void OnMaxRestore(object sender, RoutedEventArgs e) => ToggleMaximized();
|
||||
|
||||
private void OnCloseWindow(object sender, RoutedEventArgs e) => Close();
|
||||
|
||||
private void ToggleMaximized()
|
||||
{
|
||||
WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
|
||||
MaxRestoreButton.Content = WindowState == WindowState.Maximized ? "❐" : "☐";
|
||||
}
|
||||
|
||||
private void OnPaneChromeMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (sender is FrameworkElement { DataContext: ExplorerPaneViewModel pane })
|
||||
{
|
||||
Vm.ActiveTab.Activate(pane);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPathKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Enter)
|
||||
{
|
||||
Vm.GoCommand.Execute(null);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPathHistorySelected(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (sender is not ComboBox combo || e.AddedItems.Count == 0 || e.AddedItems[0] is not string path)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (NavigationTreeViewModel.PathsEqual(path, Vm.ActivePane.CurrentPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (combo.IsDropDownOpen || combo.IsKeyboardFocusWithin)
|
||||
{
|
||||
Vm.PathText = path;
|
||||
Vm.GoCommand.Execute(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSearchKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Enter)
|
||||
{
|
||||
Vm.SearchCommand.Execute(null);
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnTreeSelected(object sender, RoutedPropertyChangedEventArgs<object> e)
|
||||
{
|
||||
if (Vm.Tree.IsRevealing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.NewValue is NavNodeViewModel node)
|
||||
{
|
||||
await Vm.TreeSelectAsync(node).ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnTreeExpanded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (e.OriginalSource is TreeViewItem { DataContext: NavNodeViewModel node })
|
||||
{
|
||||
await Vm.Tree.EnsureChildrenAsync(node).ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTabChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (Tabs.SelectedItem is ExplorerTabViewModel tab)
|
||||
{
|
||||
Vm.ActiveTab = tab;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPaneSplitLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is not Grid grid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ApplySplitFromGrid(grid);
|
||||
if (Equals(grid.Tag, "split-wired"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
grid.Tag = "split-wired";
|
||||
ExplorerTabViewModel? current = null;
|
||||
PropertyChangedEventHandler? handler = null;
|
||||
|
||||
void Wire(ExplorerTabViewModel? tab)
|
||||
{
|
||||
if (current is not null && handler is not null)
|
||||
{
|
||||
current.PropertyChanged -= handler;
|
||||
}
|
||||
|
||||
current = tab;
|
||||
if (tab is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
handler = (_, args) =>
|
||||
{
|
||||
if (args.PropertyName is nameof(ExplorerTabViewModel.IsSplit)
|
||||
or nameof(ExplorerTabViewModel.SplitRatio))
|
||||
{
|
||||
ApplySplitLayout(grid, tab);
|
||||
}
|
||||
};
|
||||
tab.PropertyChanged += handler;
|
||||
ApplySplitLayout(grid, tab);
|
||||
}
|
||||
|
||||
Wire(grid.DataContext as ExplorerTabViewModel);
|
||||
grid.DataContextChanged += (_, _) => Wire(grid.DataContext as ExplorerTabViewModel);
|
||||
grid.Unloaded += (_, _) => Wire(null);
|
||||
}
|
||||
|
||||
private void OnSplitDragCompleted(object sender, DragCompletedEventArgs e)
|
||||
{
|
||||
if (sender is not GridSplitter splitter || splitter.Parent is not Grid grid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (grid.DataContext is not ExplorerTabViewModel tab || !tab.IsSplit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var leftWidth = grid.ColumnDefinitions[0].ActualWidth;
|
||||
var rightWidth = grid.ColumnDefinitions[2].ActualWidth;
|
||||
var total = leftWidth + rightWidth;
|
||||
if (total < 8)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
tab.SetSplitRatio(leftWidth / total);
|
||||
ApplySplitLayout(grid, tab);
|
||||
}
|
||||
|
||||
private static void ApplySplitFromGrid(Grid grid)
|
||||
{
|
||||
if (grid.DataContext is ExplorerTabViewModel tab)
|
||||
{
|
||||
ApplySplitLayout(grid, tab);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ApplySplitLayout(Grid grid, ExplorerTabViewModel tab)
|
||||
{
|
||||
var left = grid.ColumnDefinitions[0];
|
||||
var mid = grid.ColumnDefinitions[1];
|
||||
var right = grid.ColumnDefinitions[2];
|
||||
if (tab.IsSplit)
|
||||
{
|
||||
var ratio = Math.Clamp(tab.SplitRatio, ExplorerTabViewModel.MinSplitRatio, ExplorerTabViewModel.MaxSplitRatio);
|
||||
left.Width = new GridLength(ratio, GridUnitType.Star);
|
||||
left.MinWidth = 140;
|
||||
mid.Width = new GridLength(6);
|
||||
mid.MinWidth = 6;
|
||||
right.Width = new GridLength(1.0 - ratio, GridUnitType.Star);
|
||||
right.MinWidth = 140;
|
||||
}
|
||||
else
|
||||
{
|
||||
left.Width = new GridLength(1, GridUnitType.Star);
|
||||
left.MinWidth = 0;
|
||||
mid.Width = new GridLength(0);
|
||||
mid.MinWidth = 0;
|
||||
right.Width = new GridLength(0);
|
||||
right.MinWidth = 0;
|
||||
}
|
||||
|
||||
foreach (var splitter in grid.Children.OfType<GridSplitter>())
|
||||
{
|
||||
splitter.IsHitTestVisible = tab.IsSplit;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCloseTab(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button { Tag: ExplorerTabViewModel tab })
|
||||
{
|
||||
Vm.CloseTab(tab);
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnItemDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (sender is ListView { SelectedItem: FolderItemViewModel item })
|
||||
{
|
||||
ActivatePaneFromList((ListView)sender);
|
||||
await Vm.ActivePane.OpenItemAsync(item).ConfigureAwait(true);
|
||||
Vm.PathText = Vm.ActivePane.CurrentPath;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (sender is not ListView list)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ActivatePaneFromList(list);
|
||||
Vm.ActivePane.SelectedItems.Clear();
|
||||
foreach (FolderItemViewModel item in list.SelectedItems)
|
||||
{
|
||||
Vm.ActivePane.SelectedItems.Add(item);
|
||||
}
|
||||
|
||||
Vm.RefreshCloudActions();
|
||||
}
|
||||
|
||||
private void OnPaneFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is ListView list)
|
||||
{
|
||||
ActivatePaneFromList(list);
|
||||
}
|
||||
}
|
||||
|
||||
private void ActivatePaneFromList(ListView list)
|
||||
{
|
||||
var tab = Vm.ActiveTab;
|
||||
if (list.ItemsSource == tab.Right.Items)
|
||||
{
|
||||
tab.Activate(tab.Right);
|
||||
}
|
||||
else
|
||||
{
|
||||
tab.Activate(tab.Left);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnListMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
_dragStart = e.GetPosition(null);
|
||||
_dragPending = true;
|
||||
}
|
||||
|
||||
private void OnListDragOver(object sender, DragEventArgs e)
|
||||
{
|
||||
e.Effects = (e.KeyStates & DragDropKeyStates.ShiftKey) != 0 ? DragDropEffects.Move : DragDropEffects.Copy;
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private async void OnListDrop(object sender, DragEventArgs e)
|
||||
{
|
||||
if (!e.Data.GetDataPresent(DataFormats.FileDrop) || sender is not ListView list)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ActivatePaneFromList(list);
|
||||
var files = (string[])e.Data.GetData(DataFormats.FileDrop)!;
|
||||
var move = (e.KeyStates & DragDropKeyStates.ShiftKey) != 0 || e.AllowedEffects == DragDropEffects.Move;
|
||||
await Vm.DropAsync(files, Vm.ActivePane.CurrentPath, move).ConfigureAwait(true);
|
||||
await Vm.RefreshAsync().ConfigureAwait(true);
|
||||
}
|
||||
|
||||
protected override void OnPreviewMouseMove(MouseEventArgs e)
|
||||
{
|
||||
base.OnPreviewMouseMove(e);
|
||||
if (!_dragPending || e.LeftButton != MouseButtonState.Pressed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var pos = e.GetPosition(null);
|
||||
if (Math.Abs(pos.X - _dragStart.X) < SystemParameters.MinimumHorizontalDragDistance
|
||||
&& Math.Abs(pos.Y - _dragStart.Y) < SystemParameters.MinimumVerticalDragDistance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_dragPending = false;
|
||||
var paths = Vm.ActivePane.SelectedItems.Select(i => i.FullPath).ToArray();
|
||||
if (paths.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var data = new DataObject(DataFormats.FileDrop, paths);
|
||||
DragDrop.DoDragDrop(this, data, DragDropEffects.Copy | DragDropEffects.Move);
|
||||
}
|
||||
|
||||
private async void OnSearchDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (sender is ListView { SelectedItem: FolderItemViewModel item })
|
||||
{
|
||||
Vm.Search.IsOpen = false;
|
||||
if (item.IsDirectory)
|
||||
{
|
||||
await Vm.ActivePane.NavigateAsync(item.FullPath).ConfigureAwait(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
await Vm.ActivePane.NavigateAsync(PathRules.Parent(item.FullPath)).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
Vm.PathText = Vm.ActivePane.CurrentPath;
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnStorageTreeDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (e.OriginalSource is DependencyObject origin && IsInsideButton(origin))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Vm.Analysis.CanAct)
|
||||
{
|
||||
await Vm.OpenStorageHereAsync().ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsInsideButton(DependencyObject origin)
|
||||
{
|
||||
for (var current = origin; current is not null;)
|
||||
{
|
||||
if (current is Button)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
current = current is System.Windows.Media.Visual
|
||||
? System.Windows.Media.VisualTreeHelper.GetParent(current)
|
||||
: LogicalTreeHelper.GetParent(current);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async void OnStorageRankDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (Vm.Analysis.CanAct)
|
||||
{
|
||||
await Vm.OpenStorageHereAsync().ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCloseSearch(object sender, RoutedEventArgs e) => Vm.Search.IsOpen = false;
|
||||
private void OnCloseAnalysis(object sender, RoutedEventArgs e) => Vm.Analysis.Close();
|
||||
private void OnCloseDuplicates(object sender, RoutedEventArgs e) => Vm.Duplicates.IsOpen = false;
|
||||
|
||||
private void OnBuildIndex(object sender, RoutedEventArgs e) => Vm.BuildIndexCommand.Execute(null);
|
||||
|
||||
private void OnCtxOpen(object sender, RoutedEventArgs e) => Vm.OpenSelectedCommand.Execute(null);
|
||||
|
||||
private void OnCtxRename(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var item = Vm.ActivePane.SelectedItems.FirstOrDefault();
|
||||
if (item is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var name = PromptWindow.Ask(this, "Rename", "New name:", item.Name);
|
||||
if (!string.IsNullOrWhiteSpace(name) && name != item.Name)
|
||||
{
|
||||
Vm.RenameSelected(name);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAddNetwork(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var path = PromptWindow.Ask(this, "Add network location", "Network path (\\\\server\\share):", @"\\");
|
||||
if (!string.IsNullOrWhiteSpace(path) && path != @"\\")
|
||||
{
|
||||
Vm.PromptUnc = path;
|
||||
Vm.AddNetworkCommand.Execute(null);
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnAddOneDrive(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var picker = new Microsoft.Win32.OpenFolderDialog
|
||||
{
|
||||
Title = "Add OneDrive folder",
|
||||
Multiselect = false
|
||||
};
|
||||
if (picker.ShowDialog(this) != true || string.IsNullOrWhiteSpace(picker.FolderName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await Vm.AddCloudFolderAsync(picker.FolderName).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
private async void OnPreviewKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
var ctrl = Keyboard.Modifiers.HasFlag(ModifierKeys.Control);
|
||||
var alt = Keyboard.Modifiers.HasFlag(ModifierKeys.Alt);
|
||||
if (e.Key == Key.F5)
|
||||
{
|
||||
await Vm.RefreshAsync().ConfigureAwait(true);
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == Key.F2)
|
||||
{
|
||||
OnCtxRename(sender, e);
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (ctrl && e.Key == Key.C)
|
||||
{
|
||||
Vm.Copy();
|
||||
}
|
||||
else if (ctrl && e.Key == Key.X)
|
||||
{
|
||||
Vm.Cut();
|
||||
}
|
||||
else if (ctrl && e.Key == Key.V)
|
||||
{
|
||||
await Vm.PasteAsync().ConfigureAwait(true);
|
||||
}
|
||||
else if (e.Key == Key.Delete)
|
||||
{
|
||||
await Vm.DeleteAsync().ConfigureAwait(true);
|
||||
}
|
||||
else if (ctrl && e.Key == Key.T)
|
||||
{
|
||||
await Vm.NewTabAsync().ConfigureAwait(true);
|
||||
}
|
||||
else if (ctrl && e.Key == Key.W)
|
||||
{
|
||||
Vm.CloseTab(Vm.ActiveTab);
|
||||
}
|
||||
else if (alt && e.Key == Key.Left)
|
||||
{
|
||||
await Vm.BackAsync().ConfigureAwait(true);
|
||||
}
|
||||
else if (alt && e.Key == Key.Right)
|
||||
{
|
||||
await Vm.ForwardAsync().ConfigureAwait(true);
|
||||
}
|
||||
else if (alt && e.Key == Key.Up)
|
||||
{
|
||||
await Vm.UpAsync().ConfigureAwait(true);
|
||||
}
|
||||
else if (e.Key == Key.Enter)
|
||||
{
|
||||
await Vm.OpenSelectedAsync().ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/Explorer.App/PromptWindow.xaml
Normal file
21
src/Explorer.App/PromptWindow.xaml
Normal file
@@ -0,0 +1,21 @@
|
||||
<Window x:Class="Explorer.App.PromptWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Explorer Workbench"
|
||||
Icon="pack://application:,,,/Assets/explorer-workbench.ico"
|
||||
Height="220" Width="520"
|
||||
MinHeight="200" MinWidth="420"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Background="{DynamicResource Bg}" Foreground="{DynamicResource Fg}"
|
||||
ResizeMode="NoResize">
|
||||
<DockPanel Margin="20">
|
||||
<TextBlock x:Name="Message" DockPanel.Dock="Top" TextWrapping="Wrap" Margin="0,0,0,16"
|
||||
FontSize="13" Foreground="{DynamicResource Fg}"/>
|
||||
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,16,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"/>
|
||||
</StackPanel>
|
||||
<TextBox x:Name="Input" MinHeight="36" FontSize="14" VerticalContentAlignment="Center"
|
||||
Padding="10,8"/>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
30
src/Explorer.App/PromptWindow.xaml.cs
Normal file
30
src/Explorer.App/PromptWindow.xaml.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace Explorer.App;
|
||||
|
||||
public partial class PromptWindow : Window
|
||||
{
|
||||
public PromptWindow(string title, string message, string initial)
|
||||
{
|
||||
InitializeComponent();
|
||||
Title = title;
|
||||
Message.Text = message;
|
||||
Input.Text = initial;
|
||||
Input.SelectAll();
|
||||
Loaded += (_, _) => Input.Focus();
|
||||
}
|
||||
|
||||
public string Value => Input.Text;
|
||||
|
||||
private void OnOk(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
|
||||
public static string? Ask(Window owner, string title, string message, string initial)
|
||||
{
|
||||
var dlg = new PromptWindow(title, message, initial) { Owner = owner };
|
||||
return dlg.ShowDialog() == true ? dlg.Value : null;
|
||||
}
|
||||
}
|
||||
50
src/Explorer.App/Services/WpfClipboard.cs
Normal file
50
src/Explorer.App/Services/WpfClipboard.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using Explorer.Domain.Abstractions;
|
||||
|
||||
namespace Explorer.App.Services;
|
||||
|
||||
public sealed class WpfClipboard : IOsClipboard
|
||||
{
|
||||
public void SetFiles(IReadOnlyList<string> paths, bool cut)
|
||||
{
|
||||
var list = new StringCollection();
|
||||
foreach (var p in paths)
|
||||
{
|
||||
list.Add(p);
|
||||
}
|
||||
|
||||
var data = new DataObject();
|
||||
data.SetFileDropList(list);
|
||||
data.SetData("Preferred DropEffect", new MemoryStream([(byte)(cut ? DragDropEffects.Move : DragDropEffects.Copy), 0, 0, 0]));
|
||||
Clipboard.SetDataObject(data, copy: true);
|
||||
}
|
||||
|
||||
public bool TryGetFiles(out IReadOnlyList<string> paths, out bool cut)
|
||||
{
|
||||
paths = [];
|
||||
cut = false;
|
||||
if (!Clipboard.ContainsFileDropList())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var list = Clipboard.GetFileDropList().Cast<string>().Where(s => s is not null).Cast<string>().ToList();
|
||||
paths = list;
|
||||
try
|
||||
{
|
||||
if (Clipboard.GetData("Preferred DropEffect") is MemoryStream ms)
|
||||
{
|
||||
var b = ms.ToArray();
|
||||
cut = b.Length > 0 && b[0] == (byte)DragDropEffects.Move;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
cut = false;
|
||||
}
|
||||
|
||||
return list.Count > 0;
|
||||
}
|
||||
}
|
||||
159
src/Explorer.App/ShellIconConverter.cs
Normal file
159
src/Explorer.App/ShellIconConverter.cs
Normal file
@@ -0,0 +1,159 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Explorer.Domain;
|
||||
using Explorer.Presentation;
|
||||
|
||||
namespace Explorer.App;
|
||||
|
||||
public sealed class ShellIconConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var large = parameter is string p && p.Equals("large", StringComparison.OrdinalIgnoreCase);
|
||||
return value switch
|
||||
{
|
||||
FolderItemViewModel item => ShellIconCache.Get(item.FullPath, item.IsDirectory, large, item.MayHydrateOnRead),
|
||||
FileSystemItem fs => ShellIconCache.Get(fs.FullPath, fs.IsDirectory, large, AttributeFlags.MayHydrateOnRead(fs.Attributes)),
|
||||
string path => ShellIconCache.Get(path, Directory.Exists(path), large),
|
||||
_ => ShellIconCache.Get(null, true, large)
|
||||
};
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
=> throw new NotSupportedException();
|
||||
}
|
||||
|
||||
internal static class ShellIconCache
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, ImageSource> Cache = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public static ImageSource Get(string? path, bool isDirectory, bool large = false, bool avoidHydration = false)
|
||||
{
|
||||
var key = (large ? "L:" : "S:") + (avoidHydration ? "A:" : "") + CacheKey(path, isDirectory);
|
||||
return Cache.GetOrAdd(key, _ => Load(path, isDirectory, large, avoidHydration));
|
||||
}
|
||||
|
||||
private static string CacheKey(string? path, bool isDirectory)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path) || path == "This PC")
|
||||
{
|
||||
return isDirectory ? "dir:generic" : "file:generic";
|
||||
}
|
||||
|
||||
if (isDirectory)
|
||||
{
|
||||
return PathRules.IsDriveRoot(path) || (path.Length <= 3 && path.Contains(':', StringComparison.Ordinal))
|
||||
? "drive:" + path.TrimEnd('\\').ToUpperInvariant()
|
||||
: "dir:generic";
|
||||
}
|
||||
|
||||
var ext = Path.GetExtension(path);
|
||||
if (ext.Equals(".exe", StringComparison.OrdinalIgnoreCase)
|
||||
|| ext.Equals(".lnk", StringComparison.OrdinalIgnoreCase)
|
||||
|| ext.Equals(".ico", StringComparison.OrdinalIgnoreCase)
|
||||
|| ext.Equals(".dll", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return "path:" + path;
|
||||
}
|
||||
|
||||
return "ext:" + (string.IsNullOrEmpty(ext) ? ".file" : ext.ToLowerInvariant());
|
||||
}
|
||||
|
||||
private static ImageSource Load(string? path, bool isDirectory, bool large, bool avoidHydration = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
var info = new ShFileInfo();
|
||||
uint flags = ShgfiIcon | (large ? ShgfiLargeIcon : ShgfiSmallIcon);
|
||||
uint attrs = isDirectory ? FileAttributeDirectory : FileAttributeNormal;
|
||||
string psz;
|
||||
if (string.IsNullOrWhiteSpace(path) || path == "This PC")
|
||||
{
|
||||
psz = isDirectory ? "folder" : "file";
|
||||
flags |= ShgfiUseFileAttributes;
|
||||
}
|
||||
else if (isDirectory && PathRules.IsDriveRoot(path))
|
||||
{
|
||||
psz = PathRules.EnsureDirectoryTrailingSlashIfRoot(path);
|
||||
}
|
||||
else if (isDirectory)
|
||||
{
|
||||
psz = path;
|
||||
flags |= ShgfiUseFileAttributes;
|
||||
}
|
||||
else
|
||||
{
|
||||
psz = path;
|
||||
var ext = Path.GetExtension(path);
|
||||
if (avoidHydration
|
||||
|| !ext.Equals(".exe", StringComparison.OrdinalIgnoreCase)
|
||||
&& !ext.Equals(".lnk", StringComparison.OrdinalIgnoreCase)
|
||||
&& !ext.Equals(".ico", StringComparison.OrdinalIgnoreCase)
|
||||
&& File.Exists(path) == false)
|
||||
{
|
||||
flags |= ShgfiUseFileAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
SHGetFileInfo(psz, attrs, ref info, (uint)Marshal.SizeOf<ShFileInfo>(), flags);
|
||||
if (info.hIcon == 0)
|
||||
{
|
||||
return Fallback();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var source = Imaging.CreateBitmapSourceFromHIcon(info.hIcon, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
|
||||
source.Freeze();
|
||||
return source;
|
||||
}
|
||||
finally
|
||||
{
|
||||
DestroyIcon(info.hIcon);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Fallback();
|
||||
}
|
||||
}
|
||||
|
||||
private static ImageSource Fallback()
|
||||
{
|
||||
var bmp = BitmapSource.Create(16, 16, 96, 96, PixelFormats.Bgra32, null, new byte[16 * 16 * 4], 16 * 4);
|
||||
bmp.Freeze();
|
||||
return bmp;
|
||||
}
|
||||
|
||||
private const uint ShgfiIcon = 0x100;
|
||||
private const uint ShgfiLargeIcon = 0x0;
|
||||
private const uint ShgfiSmallIcon = 0x1;
|
||||
private const uint ShgfiUseFileAttributes = 0x10;
|
||||
private const uint FileAttributeDirectory = 0x10;
|
||||
private const uint FileAttributeNormal = 0x80;
|
||||
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern nint SHGetFileInfo(string pszPath, uint dwFileAttributes, ref ShFileInfo psfi, uint cbFileInfo, uint uFlags);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern bool DestroyIcon(nint hIcon);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct ShFileInfo
|
||||
{
|
||||
public nint hIcon;
|
||||
public int iIcon;
|
||||
public uint dwAttributes;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
|
||||
public string szDisplayName;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
|
||||
public string szTypeName;
|
||||
}
|
||||
}
|
||||
22
src/Explorer.App/Themes/Dark.xaml
Normal file
22
src/Explorer.App/Themes/Dark.xaml
Normal file
@@ -0,0 +1,22 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<SolidColorBrush x:Key="Bg" Color="#1C1C1C"/>
|
||||
<SolidColorBrush x:Key="Panel" Color="#252526"/>
|
||||
<SolidColorBrush x:Key="Fill" Color="#2D2D30"/>
|
||||
<SolidColorBrush x:Key="FillHover" Color="#3E3E42"/>
|
||||
<SolidColorBrush x:Key="InputBg" Color="#1E1E1E"/>
|
||||
<SolidColorBrush x:Key="Fg" Color="#F3F3F3"/>
|
||||
<SolidColorBrush x:Key="FgMuted" Color="#C8C8C8"/>
|
||||
<SolidColorBrush x:Key="Stroke" Color="#3F3F46"/>
|
||||
<SolidColorBrush x:Key="Accent" Color="#60CDFF"/>
|
||||
<SolidColorBrush x:Key="Danger" Color="#E81123"/>
|
||||
<SolidColorBrush x:Key="Banner" Color="#264F78"/>
|
||||
<SolidColorBrush x:Key="ActivePane" Color="#60CDFF"/>
|
||||
<SolidColorBrush x:Key="ActivePaneFill" Color="#1E2A32"/>
|
||||
<SolidColorBrush x:Key="ListSelection" Color="#264F78"/>
|
||||
<SolidColorBrush x:Key="TreeSelection" Color="#2B4B63"/>
|
||||
<SolidColorBrush x:Key="ScrollTrack" Color="#252526"/>
|
||||
<SolidColorBrush x:Key="ScrollThumb" Color="#5A5A5E"/>
|
||||
<SolidColorBrush x:Key="ScrollThumbHover" Color="#7A7A7E"/>
|
||||
<SolidColorBrush x:Key="ScrollThumbPressed" Color="#9A9A9E"/>
|
||||
</ResourceDictionary>
|
||||
22
src/Explorer.App/Themes/Light.xaml
Normal file
22
src/Explorer.App/Themes/Light.xaml
Normal file
@@ -0,0 +1,22 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<SolidColorBrush x:Key="Bg" Color="#F3F3F3"/>
|
||||
<SolidColorBrush x:Key="Panel" Color="#FFFFFF"/>
|
||||
<SolidColorBrush x:Key="Fill" Color="#E8E8E8"/>
|
||||
<SolidColorBrush x:Key="FillHover" Color="#DADADA"/>
|
||||
<SolidColorBrush x:Key="InputBg" Color="#FFFFFF"/>
|
||||
<SolidColorBrush x:Key="Fg" Color="#1A1A1A"/>
|
||||
<SolidColorBrush x:Key="FgMuted" Color="#5A5A5A"/>
|
||||
<SolidColorBrush x:Key="Stroke" Color="#D0D0D0"/>
|
||||
<SolidColorBrush x:Key="Accent" Color="#0078D4"/>
|
||||
<SolidColorBrush x:Key="Danger" Color="#C42B1C"/>
|
||||
<SolidColorBrush x:Key="Banner" Color="#D6E8F7"/>
|
||||
<SolidColorBrush x:Key="ActivePane" Color="#0078D4"/>
|
||||
<SolidColorBrush x:Key="ActivePaneFill" Color="#E8F4FC"/>
|
||||
<SolidColorBrush x:Key="ListSelection" Color="#CDE6F7"/>
|
||||
<SolidColorBrush x:Key="TreeSelection" Color="#D6E8F7"/>
|
||||
<SolidColorBrush x:Key="ScrollTrack" Color="#EDEDED"/>
|
||||
<SolidColorBrush x:Key="ScrollThumb" Color="#B0B0B0"/>
|
||||
<SolidColorBrush x:Key="ScrollThumbHover" Color="#8A8A8A"/>
|
||||
<SolidColorBrush x:Key="ScrollThumbPressed" Color="#6A6A6A"/>
|
||||
</ResourceDictionary>
|
||||
16
src/Explorer.App/app.manifest
Normal file
16
src/Explorer.App/app.manifest
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="Explorer.App"/>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
Reference in New Issue
Block a user