feat: Everything2Everything 양방향 변환 매트릭스 피보팅
EverythingToJpeg(N→JPEG 단방향)에서 Everything2Everything(N×M 매트릭스)으로 앱을 피보팅. 7개 Provider, 200+ 변환 쌍 지원. 리네이밍 - 디렉토리/솔루션/csproj/namespace/AssemblyName/MSIX Identity 일괄 치환 - 임시파일 prefix e2j_ → e2e_, 로고 라벨 E2J → E2E 아키텍처 - ConversionPair(input, output) 신규 + ProviderCapability.SupportedConversions - IConverterProvider.ConvertAsync에 outputExtension 파라미터 추가 - ProviderRegistry를 Dictionary<(input, output), Provider> 매트릭스로 재작성 + OutputsForInput / OutputsForFile 쿼리 API - ConversionEngine: 출력 ext 라우팅, 동일 입출력 자동 skip, 서브폴더 suffix를 출력 ext에서 자동 도출 - ConvertOptions를 형식별 sub-record로 분리 (Jpeg/Png/Webp/Avif/Tiff/Pdf*/Html*/Ocr) Provider 매트릭스 - MagickProvider: PNG/JPEG/WebP/AVIF/BMP/TIFF/GIF/PDF 양방향 + RAW/PSD 디코딩 (단일이미지→1페이지 PDF, GIF/TIFF→다페이지 PDF) - HeicProvider: HEIC/HEIF → 이미지 7종 - PdfProvider: PDF → 이미지 6종 (PNG임베드 후 ImageMagick 인코딩) - HtmlProvider: HTML/HTM → 이미지 + PDF (CDP printToPDF) - DocxProvider/HwpxProvider: PDF + 이미지 7종 (LibreOffice 활용) - OcrProvider 신규: 이미지/PDF → TXT/DOCX (Windows.Media.Ocr + DocumentFormat.OpenXml, PDF는 페이지별 OCR 후 결합) UI - 사이드바 TARGET FORMAT을 동적 ComboBox로 (큐 파일 매트릭스의 교집합만 표시) - 출력 형식별 색상 배지 + Quality 패널 라벨 동적 - OUTPUT DESTINATION hint도 출력 ext 기반 동적 CLI - 신규 'to <ext> <files...>' verb (예: to png photo.heic doc.docx) - help 텍스트 양방향 컨셉으로 갱신 셸 통합 - ContextMenuRegistrar 카스케이드로 재설계 (ExtendedSubCommandsKey + Everything2Everything.SubMenu.<ext>) - 입력별 매트릭스에 따라 인기 출력 10종(JPEG/PNG/WebP/PDF/TXT/DOCX/AVIF/GIF/TIFF/BMP) 동적 노출, 마지막에 '변환…' 옵션 - 메뉴 라벨 'JPEG로 변환' → 'Everything2Everything으로 변환' 기타 - Core.csproj TFM net9.0-windows → net9.0-windows10.0.19041.0 (WinRT API용) - DocumentFormat.OpenXml 3.1.0 NuGet 추가 - README 양방향 매트릭스 기준 전면 개편 - MSIX appxmanifest Description / IExplorerCommand DLL 라벨 갱신 빌드: 0 errors, 0 warnings
This commit is contained in:
parent
96861e627d
commit
9b7e5f0d0c
63 changed files with 1788 additions and 733 deletions
32
src/Everything2Everything.App/Views/DiagnoseWindow.xaml
Normal file
32
src/Everything2Everything.App/Views/DiagnoseWindow.xaml
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<ui:FluentWindow x:Class="Everything2Everything.App.Views.DiagnoseWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
Title="진단"
|
||||
Width="640" Height="540"
|
||||
ExtendsContentIntoTitleBar="True"
|
||||
WindowBackdropType="Mica"
|
||||
WindowCornerPreference="Round"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<ui:TitleBar Grid.Row="0" Title="진단"/>
|
||||
<ScrollViewer Grid.Row="1" Margin="32,8,32,16" VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel x:Name="ItemsPanel"/>
|
||||
</ScrollViewer>
|
||||
<Border Grid.Row="2" Padding="32,16"
|
||||
Background="{DynamicResource LayerOnAcrylicFillColorDefaultBrush}"
|
||||
BorderThickness="0,1,0,0"
|
||||
BorderBrush="{DynamicResource ControlStrokeColorDefaultBrush}">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<ui:Button Content="새로고침" Icon="{ui:SymbolIcon ArrowClockwise24}"
|
||||
Click="OnRefreshClick" Margin="0,0,8,0"/>
|
||||
<ui:Button Content="닫기" Click="OnCloseClick" Appearance="Primary"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ui:FluentWindow>
|
||||
118
src/Everything2Everything.App/Views/DiagnoseWindow.xaml.cs
Normal file
118
src/Everything2Everything.App/Views/DiagnoseWindow.xaml.cs
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using Everything2Everything.Core;
|
||||
using Everything2Everything.Core.Providers;
|
||||
using Wpf.Ui.Controls;
|
||||
|
||||
namespace Everything2Everything.App.Views;
|
||||
|
||||
public partial class DiagnoseWindow : FluentWindow
|
||||
{
|
||||
private readonly ConversionEngine _engine;
|
||||
|
||||
public DiagnoseWindow(ConversionEngine engine)
|
||||
{
|
||||
_engine = engine;
|
||||
InitializeComponent();
|
||||
Loaded += async (_, _) => await PopulateAsync();
|
||||
}
|
||||
|
||||
private async Task PopulateAsync()
|
||||
{
|
||||
ItemsPanel.Children.Clear();
|
||||
|
||||
ItemsPanel.Children.Add(BuildSection("환경", new[]
|
||||
{
|
||||
("OS", Environment.OSVersion.VersionString),
|
||||
(".NET", Environment.Version.ToString()),
|
||||
("실행 경로", Environment.ProcessPath ?? AppContext.BaseDirectory),
|
||||
}));
|
||||
|
||||
foreach (var provider in _engine.Providers.All)
|
||||
{
|
||||
var availability = await provider.CheckAvailabilityAsync();
|
||||
ItemsPanel.Children.Add(BuildProviderCard(provider, availability));
|
||||
}
|
||||
}
|
||||
|
||||
private static UIElement BuildSection(string title, IEnumerable<(string Key, string Value)> items)
|
||||
{
|
||||
var card = new CardControl { Padding = new Thickness(16, 12, 16, 12), Margin = new Thickness(0, 0, 0, 12) };
|
||||
var stack = new StackPanel();
|
||||
stack.Children.Add(new TextBlock
|
||||
{
|
||||
Text = title,
|
||||
FontSize = 14,
|
||||
FontWeight = FontWeights.SemiBold,
|
||||
Margin = new Thickness(0, 0, 0, 8),
|
||||
});
|
||||
foreach (var (k, v) in items)
|
||||
{
|
||||
var row = new Grid { Margin = new Thickness(0, 2, 0, 2) };
|
||||
row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(120) });
|
||||
row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||
var keyText = new TextBlock { Text = k, FontSize = 12, Foreground = (Brush)Application.Current.FindResource("TextFillColorSecondaryBrush") };
|
||||
var valueText = new TextBlock { Text = v, FontSize = 12, TextTrimming = TextTrimming.CharacterEllipsis };
|
||||
Grid.SetColumn(valueText, 1);
|
||||
row.Children.Add(keyText);
|
||||
row.Children.Add(valueText);
|
||||
stack.Children.Add(row);
|
||||
}
|
||||
card.Content = stack;
|
||||
return card;
|
||||
}
|
||||
|
||||
private UIElement BuildProviderCard(IConverterProvider provider, ProviderAvailability availability)
|
||||
{
|
||||
var card = new CardControl { Padding = new Thickness(16, 12, 16, 12), Margin = new Thickness(0, 0, 0, 12) };
|
||||
var stack = new StackPanel();
|
||||
var header = new StackPanel { Orientation = Orientation.Horizontal };
|
||||
header.Children.Add(new TextBlock
|
||||
{
|
||||
Text = provider.Capability.DisplayName,
|
||||
FontSize = 14,
|
||||
FontWeight = FontWeights.SemiBold,
|
||||
});
|
||||
var (badgeText, brushKey) = (provider.Capability.Status, availability.IsReady) switch
|
||||
{
|
||||
(ProviderStatus.ComingSoon, _) => ("개발 중", "BadgeMutedBrush"),
|
||||
(ProviderStatus.Disabled, _) => ("비활성", "BadgeMutedBrush"),
|
||||
(_, true) => ("준비됨", "BadgeReadyBrush"),
|
||||
_ => ("점검 필요", "BadgeWarnBrush"),
|
||||
};
|
||||
header.Children.Add(new Border
|
||||
{
|
||||
Background = (Brush)Application.Current.FindResource(brushKey),
|
||||
CornerRadius = new CornerRadius(10),
|
||||
Padding = new Thickness(8, 2, 8, 2),
|
||||
Margin = new Thickness(8, 0, 0, 0),
|
||||
Child = new TextBlock { Text = badgeText, FontSize = 11, Foreground = Brushes.White },
|
||||
});
|
||||
stack.Children.Add(header);
|
||||
|
||||
stack.Children.Add(new TextBlock
|
||||
{
|
||||
Text = $"입력: {string.Join(", ", provider.Capability.InputExtensions)} → 출력: {string.Join(", ", provider.Capability.OutputExtensions)}",
|
||||
FontSize = 11,
|
||||
Foreground = (Brush)Application.Current.FindResource("TextFillColorTertiaryBrush"),
|
||||
Margin = new Thickness(0, 4, 0, 0),
|
||||
});
|
||||
if (!availability.IsReady && !string.IsNullOrEmpty(availability.Reason))
|
||||
{
|
||||
stack.Children.Add(new TextBlock
|
||||
{
|
||||
Text = availability.Reason,
|
||||
FontSize = 11,
|
||||
Foreground = (Brush)Application.Current.FindResource("BadgeWarnBrush"),
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
Margin = new Thickness(0, 4, 0, 0),
|
||||
});
|
||||
}
|
||||
card.Content = stack;
|
||||
return card;
|
||||
}
|
||||
|
||||
private async void OnRefreshClick(object sender, RoutedEventArgs e) => await PopulateAsync();
|
||||
private void OnCloseClick(object sender, RoutedEventArgs e) => Close();
|
||||
}
|
||||
294
src/Everything2Everything.App/Views/FormatShiftTheme.xaml
Normal file
294
src/Everything2Everything.App/Views/FormatShiftTheme.xaml
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<!-- Backgrounds -->
|
||||
<SolidColorBrush x:Key="FsBgBase" Color="#090A0C"/>
|
||||
<SolidColorBrush x:Key="FsBgPanel" Color="#131417"/>
|
||||
<SolidColorBrush x:Key="FsBgSurface" Color="#1C1E22"/>
|
||||
<SolidColorBrush x:Key="FsBgSurfaceHover" Color="#23252A"/>
|
||||
<SolidColorBrush x:Key="FsBgInput" Color="#0F1012"/>
|
||||
|
||||
<!-- Borders -->
|
||||
<SolidColorBrush x:Key="FsBorderSubtle" Color="#26282D"/>
|
||||
<SolidColorBrush x:Key="FsBorderStrong" Color="#3A3D45"/>
|
||||
|
||||
<!-- Text -->
|
||||
<SolidColorBrush x:Key="FsTextPrimary" Color="#ECEEFA"/>
|
||||
<SolidColorBrush x:Key="FsTextSecondary" Color="#8B909A"/>
|
||||
<SolidColorBrush x:Key="FsTextTertiary" Color="#5D626C"/>
|
||||
|
||||
<!-- Accents -->
|
||||
<SolidColorBrush x:Key="FsAccentBlue" Color="#3B72FF"/>
|
||||
<SolidColorBrush x:Key="FsAccentGreen" Color="#10B981"/>
|
||||
<SolidColorBrush x:Key="FsAccentAmber" Color="#F59E0B"/>
|
||||
<SolidColorBrush x:Key="FsAccentRed" Color="#EF4444"/>
|
||||
|
||||
<!-- Format pills -->
|
||||
<SolidColorBrush x:Key="FsFmtPdf" Color="#E53E3E"/>
|
||||
<SolidColorBrush x:Key="FsFmtPng" Color="#805AD5"/>
|
||||
<SolidColorBrush x:Key="FsFmtHeic" Color="#D69E2E"/>
|
||||
<SolidColorBrush x:Key="FsFmtJpg" Color="#3182CE"/>
|
||||
<SolidColorBrush x:Key="FsFmtDocx" Color="#2B6CB0"/>
|
||||
<SolidColorBrush x:Key="FsFmtHtml" Color="#DD6B20"/>
|
||||
<SolidColorBrush x:Key="FsFmtRaw" Color="#319795"/>
|
||||
<SolidColorBrush x:Key="FsFmtGif" Color="#9F7AEA"/>
|
||||
<SolidColorBrush x:Key="FsFmtTiff" Color="#38B2AC"/>
|
||||
<SolidColorBrush x:Key="FsFmtWebp" Color="#48BB78"/>
|
||||
<SolidColorBrush x:Key="FsFmtBmp" Color="#718096"/>
|
||||
<SolidColorBrush x:Key="FsFmtAvif" Color="#ED64A6"/>
|
||||
<SolidColorBrush x:Key="FsFmtHwp" Color="#9B2C2C"/>
|
||||
<SolidColorBrush x:Key="FsFmtOther" Color="#4A5568"/>
|
||||
|
||||
<!-- Font families -->
|
||||
<FontFamily x:Key="FsFontSans">Inter, Segoe UI Variable Text, Segoe UI</FontFamily>
|
||||
<FontFamily x:Key="FsFontMono">JetBrains Mono, Cascadia Mono, Consolas</FontFamily>
|
||||
|
||||
<!-- Typography styles -->
|
||||
<Style x:Key="FsLabelStyle" TargetType="TextBlock">
|
||||
<Setter Property="FontFamily" Value="{StaticResource FsFontSans}"/>
|
||||
<Setter Property="FontSize" Value="11"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource FsTextSecondary}"/>
|
||||
<Setter Property="TextOptions.TextFormattingMode" Value="Display"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="FsCaptionStyle" TargetType="TextBlock">
|
||||
<Setter Property="FontFamily" Value="{StaticResource FsFontSans}"/>
|
||||
<Setter Property="FontSize" Value="11"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource FsTextTertiary}"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="FsBodyStyle" TargetType="TextBlock">
|
||||
<Setter Property="FontFamily" Value="{StaticResource FsFontSans}"/>
|
||||
<Setter Property="FontSize" Value="13"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource FsTextPrimary}"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="FsMonoStyle" TargetType="TextBlock">
|
||||
<Setter Property="FontFamily" Value="{StaticResource FsFontMono}"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource FsTextSecondary}"/>
|
||||
</Style>
|
||||
|
||||
<!-- Slider style: 2px track, 14px round thumb, blue fill up to value -->
|
||||
<Style x:Key="FsSliderStyle" TargetType="Slider">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource FsAccentBlue}"/>
|
||||
<Setter Property="MinHeight" Value="20"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="IsMoveToPointEnabled" Value="True"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Slider">
|
||||
<Grid>
|
||||
<Border Height="2" CornerRadius="1"
|
||||
Background="{StaticResource FsBorderStrong}"
|
||||
VerticalAlignment="Center"/>
|
||||
<Track x:Name="PART_Track">
|
||||
<Track.DecreaseRepeatButton>
|
||||
<RepeatButton Command="Slider.DecreaseLarge">
|
||||
<RepeatButton.Template>
|
||||
<ControlTemplate TargetType="RepeatButton">
|
||||
<Border Height="2" CornerRadius="1"
|
||||
Background="{StaticResource FsAccentBlue}"
|
||||
VerticalAlignment="Center"/>
|
||||
</ControlTemplate>
|
||||
</RepeatButton.Template>
|
||||
</RepeatButton>
|
||||
</Track.DecreaseRepeatButton>
|
||||
<Track.Thumb>
|
||||
<Thumb>
|
||||
<Thumb.Template>
|
||||
<ControlTemplate TargetType="Thumb">
|
||||
<Ellipse Width="14" Height="14"
|
||||
Fill="{StaticResource FsTextPrimary}"/>
|
||||
</ControlTemplate>
|
||||
</Thumb.Template>
|
||||
</Thumb>
|
||||
</Track.Thumb>
|
||||
<Track.IncreaseRepeatButton>
|
||||
<RepeatButton Command="Slider.IncreaseLarge">
|
||||
<RepeatButton.Template>
|
||||
<ControlTemplate TargetType="RepeatButton">
|
||||
<Border Background="Transparent"/>
|
||||
</ControlTemplate>
|
||||
</RepeatButton.Template>
|
||||
</RepeatButton>
|
||||
</Track.IncreaseRepeatButton>
|
||||
</Track>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- Tab pill style -->
|
||||
<Style x:Key="FsTabPillStyle" TargetType="ToggleButton">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource FsTextSecondary}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="14,6"/>
|
||||
<Setter Property="FontSize" Value="13"/>
|
||||
<Setter Property="FontWeight" Value="Medium"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ToggleButton">
|
||||
<Border x:Name="PART_Bd" CornerRadius="20"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Background="{TemplateBinding Background}">
|
||||
<ContentPresenter HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
TextElement.Foreground="{TemplateBinding Foreground}"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter TargetName="PART_Bd" Property="Background" Value="{StaticResource FsAccentBlue}"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="PART_Bd" Property="Background" Value="{StaticResource FsBgSurfaceHover}"/>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsChecked" Value="True"/>
|
||||
<Condition Property="IsMouseOver" Value="True"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="PART_Bd" Property="Background" Value="{StaticResource FsAccentBlue}"/>
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- Segment button style (Skip / Rename / Replace) -->
|
||||
<Style x:Key="FsSegmentStyle" TargetType="ToggleButton">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource FsTextSecondary}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="FontWeight" Value="Medium"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ToggleButton">
|
||||
<Border x:Name="PART_Bd" CornerRadius="4" Padding="0,6">
|
||||
<ContentPresenter HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
TextElement.Foreground="{TemplateBinding Foreground}"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter TargetName="PART_Bd" Property="Background" Value="{StaticResource FsBgSurface}"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource FsTextPrimary}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- Primary button (Processing Queue / Run) -->
|
||||
<Style x:Key="FsPrimaryButtonStyle" TargetType="Button">
|
||||
<Setter Property="Background" Value="{StaticResource FsAccentBlue}"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="12"/>
|
||||
<Setter Property="FontSize" Value="13"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border x:Name="PART_Bd"
|
||||
Background="{TemplateBinding Background}"
|
||||
CornerRadius="6"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
TextElement.Foreground="{TemplateBinding Foreground}"
|
||||
TextElement.FontSize="{TemplateBinding FontSize}"
|
||||
TextElement.FontWeight="{TemplateBinding FontWeight}"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter TargetName="PART_Bd" Property="Opacity" Value="0.5"/>
|
||||
<Setter Property="Cursor" Value="Arrow"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="PART_Bd" Property="Background" Value="#2D5DDB"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- Secondary button -->
|
||||
<Style x:Key="FsSecondaryButtonStyle" TargetType="Button">
|
||||
<Setter Property="Background" Value="{StaticResource FsBgSurface}"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource FsTextPrimary}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource FsBorderSubtle}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="12,6"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="FontWeight" Value="Medium"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border x:Name="PART_Bd"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="6"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
TextElement.Foreground="{TemplateBinding Foreground}"
|
||||
TextElement.FontSize="{TemplateBinding FontSize}"
|
||||
TextElement.FontWeight="{TemplateBinding FontWeight}"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="PART_Bd" Property="Background" Value="{StaticResource FsBgSurfaceHover}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- Tab badge -->
|
||||
<Style x:Key="FsTabBadgeStyle" TargetType="Border">
|
||||
<Setter Property="CornerRadius" Value="10"/>
|
||||
<Setter Property="Padding" Value="6,1"/>
|
||||
<Setter Property="Background" Value="{StaticResource FsBgSurface}"/>
|
||||
</Style>
|
||||
|
||||
<!-- Path input box -->
|
||||
<Style x:Key="FsPathInputStyle" TargetType="TextBox">
|
||||
<Setter Property="Background" Value="{StaticResource FsBgInput}"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource FsTextSecondary}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource FsBorderSubtle}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="10,8"/>
|
||||
<Setter Property="FontFamily" Value="{StaticResource FsFontMono}"/>
|
||||
<Setter Property="FontSize" Value="11"/>
|
||||
<Setter Property="CaretBrush" Value="{StaticResource FsTextPrimary}"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="TextBox">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="6"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ScrollViewer x:Name="PART_ContentHost"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
681
src/Everything2Everything.App/Views/MainWindow.xaml
Normal file
681
src/Everything2Everything.App/Views/MainWindow.xaml
Normal file
|
|
@ -0,0 +1,681 @@
|
|||
<ui:FluentWindow x:Class="Everything2Everything.App.Views.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
Title="FormatShift Utility"
|
||||
Width="1280" Height="960"
|
||||
MinWidth="1080" MinHeight="640"
|
||||
ExtendsContentIntoTitleBar="True"
|
||||
WindowBackdropType="None"
|
||||
WindowCornerPreference="Round"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
AllowDrop="True"
|
||||
Drop="OnFilesDropped"
|
||||
DragOver="OnDragOver"
|
||||
DragLeave="OnDragLeave"
|
||||
TextOptions.TextFormattingMode="Display"
|
||||
UseLayoutRounding="True">
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="FormatShiftTheme.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
|
||||
<Window.InputBindings>
|
||||
<KeyBinding Key="O" Modifiers="Ctrl" Command="{Binding AddFilesCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<KeyBinding Key="Enter" Modifiers="Ctrl" Command="{Binding ProcessQueueCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<KeyBinding Key="Escape" Command="{Binding CloseCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<KeyBinding Key="F5" Command="{Binding RefreshCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
</Window.InputBindings>
|
||||
|
||||
<Grid Background="{StaticResource FsBgBase}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Title bar (window controls only) -->
|
||||
<ui:TitleBar Grid.Row="0" Title="" ShowMaximize="True" ShowMinimize="True"/>
|
||||
|
||||
<!-- App body -->
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="320"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- ============== SIDEBAR ============== -->
|
||||
<Border Grid.Column="0"
|
||||
Background="{StaticResource FsBgPanel}"
|
||||
BorderBrush="{StaticResource FsBorderSubtle}"
|
||||
BorderThickness="0,0,1,0">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="56"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Sidebar header -->
|
||||
<Border Grid.Row="0" BorderBrush="{StaticResource FsBorderSubtle}"
|
||||
BorderThickness="0,0,0,1" Padding="24,0">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<Path Width="18" Height="18" Stretch="Uniform"
|
||||
Stroke="{StaticResource FsAccentBlue}" StrokeThickness="2"
|
||||
Data="M3,3 L21,3 L21,21 L3,21 Z M12,8 L12,16 M8,12 L16,12"/>
|
||||
<TextBlock Margin="8,0,0,0" FontSize="14" FontWeight="SemiBold"
|
||||
Foreground="{StaticResource FsTextPrimary}"
|
||||
FontFamily="{StaticResource FsFontSans}">
|
||||
FormatShift
|
||||
<Run Foreground="{StaticResource FsTextTertiary}" FontWeight="Medium"
|
||||
Text=" Utility"/>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Sidebar content -->
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" Padding="24,24,24,24">
|
||||
<StackPanel>
|
||||
<!-- Target Format -->
|
||||
<StackPanel Margin="0,0,0,32">
|
||||
<TextBlock Text="TARGET FORMAT" Style="{StaticResource FsLabelStyle}"/>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border x:Name="OutputFormatBadge" Grid.Column="0"
|
||||
Width="36" Height="28" CornerRadius="4"
|
||||
Background="{StaticResource FsFmtJpg}"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock x:Name="OutputFormatBadgeText"
|
||||
Text="JPG" Foreground="White" FontSize="9"
|
||||
FontWeight="SemiBold"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ComboBox x:Name="OutputFormatCombo" Grid.Column="1"
|
||||
Margin="8,0,0,0" VerticalAlignment="Center"
|
||||
SelectionChanged="OnOutputFormatChanged"/>
|
||||
</Grid>
|
||||
<TextBlock x:Name="OutputFormatHint" Margin="0,6,0,0"
|
||||
Style="{StaticResource FsCaptionStyle}"
|
||||
Text="모든 입력에서 변환 가능한 형식이 표시됩니다"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Encoding Quality -->
|
||||
<StackPanel x:Name="QualityPanel" Margin="0,0,0,32">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Name="QualityLabelText" Text="ENCODING QUALITY" Style="{StaticResource FsLabelStyle}"/>
|
||||
<Border Grid.Column="1" CornerRadius="4"
|
||||
Background="{StaticResource FsBgInput}"
|
||||
BorderBrush="{StaticResource FsBorderSubtle}"
|
||||
BorderThickness="1" Padding="6,2">
|
||||
<TextBlock x:Name="QualityValueText"
|
||||
FontFamily="{StaticResource FsFontMono}" FontSize="12"
|
||||
Foreground="{StaticResource FsAccentBlue}"
|
||||
Text="85%"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
<Slider x:Name="QualitySlider" Margin="0,12,0,0"
|
||||
Style="{StaticResource FsSliderStyle}"
|
||||
Minimum="1" Maximum="100" Value="85"
|
||||
ValueChanged="OnQualityChanged"/>
|
||||
<Grid Margin="0,4,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="Smaller File" Style="{StaticResource FsCaptionStyle}"/>
|
||||
<TextBlock Grid.Column="1" Text="Higher Quality" Style="{StaticResource FsCaptionStyle}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Output Destination -->
|
||||
<StackPanel Margin="0,0,0,32">
|
||||
<TextBlock Text="OUTPUT DESTINATION" Style="{StaticResource FsLabelStyle}"/>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox x:Name="OutputPathTextBox"
|
||||
Style="{StaticResource FsPathInputStyle}"
|
||||
ToolTip="비워두면 원본 폴더 옆 서브폴더에 저장"/>
|
||||
<Button Grid.Column="1" Margin="8,0,0,0" Width="36"
|
||||
Content="…" Style="{StaticResource FsSecondaryButtonStyle}"
|
||||
Click="OnPickOutputFolderClick"/>
|
||||
</Grid>
|
||||
<TextBlock x:Name="OutputDestHint" Margin="0,6,0,0"
|
||||
Style="{StaticResource FsCaptionStyle}"
|
||||
Text="비워두면 원본 옆 _jpg 폴더에 저장됩니다"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- File Conflict Rule -->
|
||||
<StackPanel Margin="0,0,0,32">
|
||||
<TextBlock Text="FILE CONFLICT RULE" Style="{StaticResource FsLabelStyle}"/>
|
||||
<Border Margin="0,8,0,0"
|
||||
Background="{StaticResource FsBgInput}"
|
||||
BorderBrush="{StaticResource FsBorderSubtle}"
|
||||
BorderThickness="1" CornerRadius="6" Padding="2">
|
||||
<UniformGrid Rows="1" Columns="3">
|
||||
<ToggleButton x:Name="ConflictSkipBtn" Content="Skip"
|
||||
Style="{StaticResource FsSegmentStyle}"
|
||||
Click="OnConflictSegmentClick"
|
||||
Tag="Skip"/>
|
||||
<ToggleButton x:Name="ConflictRenameBtn" Content="Rename"
|
||||
Style="{StaticResource FsSegmentStyle}"
|
||||
IsChecked="True"
|
||||
Click="OnConflictSegmentClick"
|
||||
Tag="Rename"/>
|
||||
<ToggleButton x:Name="ConflictReplaceBtn" Content="Replace"
|
||||
Style="{StaticResource FsSegmentStyle}"
|
||||
Click="OnConflictSegmentClick"
|
||||
Tag="Replace"/>
|
||||
</UniformGrid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Stats Box -->
|
||||
<Border Background="{StaticResource FsBgInput}"
|
||||
BorderBrush="{StaticResource FsBorderSubtle}"
|
||||
BorderThickness="1" CornerRadius="6" Padding="12">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel>
|
||||
<TextBlock Text="PROCESSED TODAY"
|
||||
FontFamily="{StaticResource FsFontSans}"
|
||||
FontSize="10" FontWeight="SemiBold"
|
||||
Foreground="{StaticResource FsTextSecondary}"/>
|
||||
<TextBlock x:Name="ProcessedTodayText"
|
||||
FontFamily="{StaticResource FsFontMono}"
|
||||
FontSize="14"
|
||||
Foreground="{StaticResource FsTextPrimary}"
|
||||
Text="0" Margin="0,4,0,0"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Text="EST. SPACE SAVED"
|
||||
FontFamily="{StaticResource FsFontSans}"
|
||||
FontSize="10" FontWeight="SemiBold"
|
||||
Foreground="{StaticResource FsTextSecondary}"/>
|
||||
<TextBlock x:Name="SpaceSavedText"
|
||||
FontFamily="{StaticResource FsFontMono}"
|
||||
FontSize="14"
|
||||
Foreground="{StaticResource FsAccentGreen}"
|
||||
Text="0 B" Margin="0,4,0,0"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- Sidebar footer -->
|
||||
<Border Grid.Row="2" BorderBrush="{StaticResource FsBorderSubtle}"
|
||||
BorderThickness="0,1,0,0" Padding="24">
|
||||
<StackPanel>
|
||||
<TextBlock x:Name="CapabilityStatusText" Margin="0,0,0,12"
|
||||
Style="{StaticResource FsCaptionStyle}"
|
||||
TextWrapping="Wrap" Visibility="Collapsed"/>
|
||||
<Button x:Name="ProcessQueueButton"
|
||||
Content="Idle — drop files to begin"
|
||||
Style="{StaticResource FsPrimaryButtonStyle}"
|
||||
IsEnabled="False"
|
||||
Click="OnProcessQueueClick"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- ============== MAIN ============== -->
|
||||
<Grid Grid.Column="1" Background="{StaticResource FsBgBase}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="56"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Nav bar -->
|
||||
<Border Grid.Row="0" BorderBrush="{StaticResource FsBorderSubtle}"
|
||||
BorderThickness="0,0,0,1" Padding="24,0">
|
||||
<Grid VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Tabs -->
|
||||
<Border HorizontalAlignment="Left"
|
||||
Background="{StaticResource FsBgPanel}"
|
||||
BorderBrush="{StaticResource FsBorderSubtle}"
|
||||
BorderThickness="1" CornerRadius="20" Padding="4">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<ToggleButton x:Name="TabActiveBtn"
|
||||
Style="{StaticResource FsTabPillStyle}"
|
||||
Click="OnTabClick" Tag="Active">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Active Queue" VerticalAlignment="Center"/>
|
||||
<Border Margin="8,0,0,0" Style="{StaticResource FsTabBadgeStyle}">
|
||||
<TextBlock x:Name="TabActiveBadge" Text="0"
|
||||
FontSize="10" FontWeight="SemiBold"
|
||||
Foreground="{StaticResource FsTextTertiary}"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ToggleButton>
|
||||
<ToggleButton x:Name="TabPastBtn" Margin="8,0,0,0"
|
||||
IsChecked="True"
|
||||
Style="{StaticResource FsTabPillStyle}"
|
||||
Click="OnTabClick" Tag="Past">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Past Results" VerticalAlignment="Center"/>
|
||||
<Border Margin="8,0,0,0" Style="{StaticResource FsTabBadgeStyle}">
|
||||
<TextBlock x:Name="TabPastBadge" Text="0"
|
||||
FontSize="10" FontWeight="SemiBold"
|
||||
Foreground="{StaticResource FsTextTertiary}"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Actions -->
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal">
|
||||
<Button Content="Register Menu" Margin="0,0,8,0"
|
||||
Style="{StaticResource FsSecondaryButtonStyle}"
|
||||
Click="OnRegisterClick"/>
|
||||
<Button Content="Diagnose" Margin="0,0,8,0"
|
||||
Style="{StaticResource FsSecondaryButtonStyle}"
|
||||
Click="OnDiagnoseClick"/>
|
||||
<Button Content="Export Log" Margin="0,0,8,0"
|
||||
Style="{StaticResource FsSecondaryButtonStyle}"
|
||||
Click="OnExportLogClick"/>
|
||||
<Button x:Name="ClearAllButton" Content="Clear All"
|
||||
Style="{StaticResource FsSecondaryButtonStyle}"
|
||||
Click="OnClearAllClick"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- View content (좌측: 탭 컨텐츠 / 우측: Preview 컬럼) -->
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" MinWidth="360"/>
|
||||
<ColumnDefinition Width="380"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 좌측: Active Queue OR Past Results -->
|
||||
<Grid Grid.Column="0">
|
||||
<!-- Active Queue view -->
|
||||
<Grid x:Name="ActiveQueueView" Visibility="Collapsed">
|
||||
<Grid x:Name="DropZoneEmpty">
|
||||
<Border Background="{StaticResource FsBgBase}">
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Margin="48">
|
||||
<Path Width="48" Height="48" Stretch="Uniform"
|
||||
Stroke="{StaticResource FsAccentBlue}" StrokeThickness="1.5"
|
||||
Data="M21,15 L21,19 C21,20.1 20.1,21 19,21 L5,21 C3.9,21 3,20.1 3,19 L3,15 M7,10 L12,15 L17,10 M12,15 L12,3"/>
|
||||
<TextBlock Margin="0,16,0,0" FontSize="16" FontWeight="SemiBold"
|
||||
Foreground="{StaticResource FsTextPrimary}"
|
||||
HorizontalAlignment="Center"
|
||||
Text="Drop files anywhere to queue"/>
|
||||
<TextBlock Margin="0,4,0,0" Style="{StaticResource FsCaptionStyle}"
|
||||
HorizontalAlignment="Center"
|
||||
Text="PNG · JPG · GIF · HEIC · RAW · PDF · DOCX · HTML · HWP/HWPX"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
<ScrollViewer x:Name="ActiveQueueScroll" VerticalScrollBarVisibility="Auto"
|
||||
Padding="24" Visibility="Collapsed">
|
||||
<ItemsControl x:Name="ActiveQueueList">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Margin="0,0,0,8" Padding="12,10" CornerRadius="6"
|
||||
Background="{StaticResource FsBgPanel}"
|
||||
BorderBrush="{StaticResource FsBorderSubtle}"
|
||||
BorderThickness="1"
|
||||
Cursor="Hand"
|
||||
PreviewMouseLeftButtonUp="OnQueueRowClick"
|
||||
Tag="{Binding}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="40"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition Width="120"/>
|
||||
<ColumnDefinition Width="40"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border Width="32" Height="32" CornerRadius="4"
|
||||
Background="{Binding FormatBrush}"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding FormatLabel}"
|
||||
Foreground="White" FontSize="10"
|
||||
FontWeight="SemiBold"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<StackPanel Grid.Column="1" Margin="16,0,0,0"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding FileName}"
|
||||
Style="{StaticResource FsBodyStyle}"
|
||||
FontWeight="Medium"
|
||||
TextTrimming="CharacterEllipsis"/>
|
||||
<TextBlock Text="{Binding MetaLine}"
|
||||
Style="{StaticResource FsCaptionStyle}"
|
||||
Margin="0,2,0,0"/>
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Column="2" Text="{Binding SizeText}"
|
||||
Style="{StaticResource FsMonoStyle}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Column="3" Text="{Binding StateText}"
|
||||
FontFamily="{StaticResource FsFontMono}"
|
||||
FontSize="12"
|
||||
Foreground="{Binding StateBrush}"
|
||||
VerticalAlignment="Center"/>
|
||||
<Button Grid.Column="4"
|
||||
Style="{StaticResource FsSecondaryButtonStyle}"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Padding="6"
|
||||
Click="OnRemoveQueueItem"
|
||||
Tag="{Binding}">
|
||||
<Path Width="14" Height="14" Stretch="Uniform"
|
||||
Stroke="{StaticResource FsTextTertiary}"
|
||||
StrokeThickness="2"
|
||||
Data="M18,6 L6,18 M6,6 L18,18"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
<!-- Past Results view (default) -->
|
||||
<ScrollViewer x:Name="PastResultsView" VerticalScrollBarVisibility="Auto"
|
||||
Padding="24">
|
||||
<ItemsControl x:Name="PastResultsList">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Margin="0,0,0,40">
|
||||
<!-- Date header -->
|
||||
<Grid Margin="0,0,0,12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding DateTitle}"
|
||||
FontFamily="{StaticResource FsFontSans}"
|
||||
FontSize="14" FontWeight="SemiBold"
|
||||
Foreground="{StaticResource FsTextPrimary}"/>
|
||||
<Border Grid.Column="1" CornerRadius="20" Padding="10,4"
|
||||
Background="#10B98122">
|
||||
<TextBlock Text="{Binding SessionSavingsText}"
|
||||
FontFamily="{StaticResource FsFontMono}"
|
||||
FontSize="12"
|
||||
Foreground="{StaticResource FsAccentGreen}"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
<Border Height="1" Background="{StaticResource FsBorderSubtle}"
|
||||
Margin="0,0,0,12"/>
|
||||
|
||||
<!-- Rows -->
|
||||
<ItemsControl ItemsSource="{Binding Entries}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border BorderBrush="#0DFFFFFF"
|
||||
BorderThickness="0,0,0,1" Padding="0,12"
|
||||
Cursor="Hand"
|
||||
Background="Transparent"
|
||||
PreviewMouseLeftButtonUp="OnPastRowClick"
|
||||
Tag="{Binding}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="40"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition Width="120"/>
|
||||
<ColumnDefinition Width="40"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border Width="32" Height="32" CornerRadius="4"
|
||||
Background="{Binding FormatBrush}"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding FormatLabel}"
|
||||
Foreground="White" FontSize="10"
|
||||
FontWeight="SemiBold"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
|
||||
<StackPanel Grid.Column="1" Margin="16,0,0,0"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding FileName}"
|
||||
Style="{StaticResource FsBodyStyle}"
|
||||
FontWeight="Medium"
|
||||
TextTrimming="CharacterEllipsis"/>
|
||||
<TextBlock Text="{Binding MetaLine}"
|
||||
Style="{StaticResource FsCaptionStyle}"
|
||||
Margin="0,2,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding SizeText}"
|
||||
Style="{StaticResource FsMonoStyle}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"/>
|
||||
|
||||
<TextBlock Grid.Column="3"
|
||||
Text="{Binding SavingsText}"
|
||||
FontFamily="{StaticResource FsFontMono}"
|
||||
FontSize="12"
|
||||
Foreground="{StaticResource FsAccentGreen}"
|
||||
VerticalAlignment="Center"/>
|
||||
|
||||
<StackPanel Grid.Column="4" Orientation="Horizontal"
|
||||
VerticalAlignment="Center">
|
||||
<Path Width="12" Height="12" Stretch="Uniform"
|
||||
Stroke="{StaticResource FsAccentGreen}"
|
||||
StrokeThickness="3"
|
||||
Data="M20,6 L9,17 L4,12"/>
|
||||
<TextBlock Text="Success" Margin="6,0,0,0"
|
||||
FontSize="11" FontWeight="Medium"
|
||||
Foreground="{StaticResource FsAccentGreen}"/>
|
||||
</StackPanel>
|
||||
|
||||
<Button Grid.Column="5"
|
||||
Style="{StaticResource FsSecondaryButtonStyle}"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Padding="6"
|
||||
Click="OnOpenFolderClick"
|
||||
Tag="{Binding SourcePath}"
|
||||
ToolTip="원본 폴더 열기">
|
||||
<Path Width="14" Height="14" Stretch="Uniform"
|
||||
Stroke="{StaticResource FsTextTertiary}"
|
||||
StrokeThickness="2"
|
||||
Data="M22,19 C22,20.1 21.1,21 20,21 L4,21 C2.9,21 2,20.1 2,19 L2,5 C2,3.9 2.9,3 4,3 L9,3 L11,6 L20,6 C21.1,6 22,6.9 22,8 Z"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
<!-- /좌측 컬럼 끝 -->
|
||||
|
||||
<!-- 우측: Preview 컬럼 (항상 보임) -->
|
||||
<Border Grid.Column="1"
|
||||
Background="{StaticResource FsBgPanel}"
|
||||
BorderBrush="{StaticResource FsBorderSubtle}"
|
||||
BorderThickness="1,0,0,0">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 헤더 -->
|
||||
<Border Grid.Row="0" Padding="20,16,20,12"
|
||||
BorderBrush="{StaticResource FsBorderSubtle}"
|
||||
BorderThickness="0,0,0,1">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<Path Width="14" Height="14" Stretch="Uniform"
|
||||
Stroke="{StaticResource FsTextSecondary}" StrokeThickness="2"
|
||||
Data="M1,12 C1,12 5,4 12,4 C19,4 23,12 23,12 C23,12 19,20 12,20 C5,20 1,12 1,12 Z M12,9 A3,3 0 1,1 12,15 A3,3 0 1,1 12,9 Z"/>
|
||||
<TextBlock Text="PREVIEW" Margin="8,0,0,0"
|
||||
Style="{StaticResource FsLabelStyle}"
|
||||
VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 이미지 영역 -->
|
||||
<Grid Grid.Row="1" Margin="20,16,20,16" x:Name="PreviewImageArea">
|
||||
<Border Background="{StaticResource FsBgInput}"
|
||||
BorderBrush="{StaticResource FsBorderSubtle}"
|
||||
BorderThickness="1" CornerRadius="6">
|
||||
<Grid>
|
||||
<!-- Empty state -->
|
||||
<StackPanel x:Name="PreviewEmpty" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="20">
|
||||
<Path Width="36" Height="36" Stretch="Uniform"
|
||||
Stroke="{StaticResource FsTextTertiary}" StrokeThickness="1.5"
|
||||
Data="M1,12 C1,12 5,4 12,4 C19,4 23,12 23,12 C23,12 19,20 12,20 C5,20 1,12 1,12 Z M12,9 A3,3 0 1,1 12,15 A3,3 0 1,1 12,9 Z"/>
|
||||
<TextBlock Margin="0,12,0,0" FontSize="13" FontWeight="SemiBold"
|
||||
Foreground="{StaticResource FsTextSecondary}"
|
||||
HorizontalAlignment="Center"
|
||||
Text="No selection"/>
|
||||
<TextBlock Margin="0,4,0,0" Style="{StaticResource FsCaptionStyle}"
|
||||
HorizontalAlignment="Center" TextAlignment="Center"
|
||||
TextWrapping="Wrap" MaxWidth="240"
|
||||
Text="Active Queue 항목을 클릭하면 여기에 미리보기가 표시됩니다."/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Loading -->
|
||||
<StackPanel x:Name="PreviewLoading" Visibility="Collapsed"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<ProgressBar IsIndeterminate="True" Width="180" Height="3"
|
||||
Foreground="{StaticResource FsAccentBlue}"
|
||||
Background="{StaticResource FsBgSurface}"/>
|
||||
<TextBlock Margin="0,12,0,0" Style="{StaticResource FsCaptionStyle}"
|
||||
HorizontalAlignment="Center"
|
||||
Text="미리보기 생성 중…"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Image -->
|
||||
<Image x:Name="PreviewImage" Stretch="Uniform" Margin="16"
|
||||
Visibility="Collapsed"/>
|
||||
|
||||
<!-- Reason -->
|
||||
<StackPanel x:Name="PreviewReason" Visibility="Collapsed"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center" Margin="20">
|
||||
<Path Width="32" Height="32" Stretch="Uniform"
|
||||
Stroke="{StaticResource FsAccentAmber}" StrokeThickness="1.5"
|
||||
Data="M12,2 L22,20 L2,20 Z M12,9 L12,14 M12,17 L12,17.01"/>
|
||||
<TextBlock x:Name="PreviewReasonText" Margin="0,12,0,0"
|
||||
Style="{StaticResource FsBodyStyle}"
|
||||
FontSize="12"
|
||||
HorizontalAlignment="Center" TextAlignment="Center"
|
||||
TextWrapping="Wrap" MaxWidth="280"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<!-- 메타 -->
|
||||
<Border Grid.Row="2" Padding="20,12,20,20"
|
||||
BorderBrush="{StaticResource FsBorderSubtle}"
|
||||
BorderThickness="0,1,0,0">
|
||||
<StackPanel>
|
||||
<TextBlock x:Name="PreviewFileName"
|
||||
Style="{StaticResource FsBodyStyle}"
|
||||
FontWeight="SemiBold" TextWrapping="NoWrap"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Text="—"/>
|
||||
<TextBlock x:Name="PreviewFilePath" Margin="0,4,0,0"
|
||||
FontFamily="{StaticResource FsFontMono}" FontSize="11"
|
||||
Foreground="{StaticResource FsTextTertiary}"
|
||||
TextTrimming="CharacterEllipsis" MaxHeight="32"/>
|
||||
|
||||
<Border Margin="0,12,0,12" Height="1"
|
||||
Background="{StaticResource FsBorderSubtle}"/>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Format"
|
||||
Style="{StaticResource FsCaptionStyle}" Margin="0,0,0,4"/>
|
||||
<TextBlock x:Name="PreviewFormatText" Grid.Row="0" Grid.Column="1"
|
||||
FontFamily="{StaticResource FsFontMono}" FontSize="12"
|
||||
Foreground="{StaticResource FsTextPrimary}" Margin="0,0,0,4"/>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Size"
|
||||
Style="{StaticResource FsCaptionStyle}" Margin="0,0,0,4"/>
|
||||
<TextBlock x:Name="PreviewSizeText" Grid.Row="1" Grid.Column="1"
|
||||
FontFamily="{StaticResource FsFontMono}" FontSize="12"
|
||||
Foreground="{StaticResource FsTextPrimary}" Margin="0,0,0,4"/>
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Dimensions"
|
||||
Style="{StaticResource FsCaptionStyle}" Margin="0,0,0,4"/>
|
||||
<TextBlock x:Name="PreviewDimText" Grid.Row="2" Grid.Column="1"
|
||||
FontFamily="{StaticResource FsFontMono}" FontSize="12"
|
||||
Foreground="{StaticResource FsTextPrimary}" Text="—"
|
||||
Margin="0,0,0,4"/>
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="Pages"
|
||||
Style="{StaticResource FsCaptionStyle}"/>
|
||||
<TextBlock x:Name="PreviewPageText" Grid.Row="3" Grid.Column="1"
|
||||
FontFamily="{StaticResource FsFontMono}" FontSize="12"
|
||||
Foreground="{StaticResource FsTextPrimary}" Text="—"/>
|
||||
</Grid>
|
||||
|
||||
<Button Content="Open in Explorer" Margin="0,16,0,0"
|
||||
Style="{StaticResource FsSecondaryButtonStyle}"
|
||||
Click="OnPreviewOpenFolder"
|
||||
HorizontalAlignment="Stretch"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- Drop hint overlay (전체 덮음) -->
|
||||
<Border x:Name="DropHintOverlay" Visibility="Collapsed" Grid.ColumnSpan="2"
|
||||
Background="#CC090A0C" IsHitTestVisible="False">
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Path Width="64" Height="64" Stretch="Uniform"
|
||||
Stroke="{StaticResource FsAccentBlue}" StrokeThickness="2"
|
||||
Data="M21,15 L21,19 C21,20.1 20.1,21 19,21 L5,21 C3.9,21 3,20.1 3,19 L3,15 M7,10 L12,15 L17,10 M12,15 L12,3"/>
|
||||
<TextBlock Text="Drop to add to queue" Margin="0,16,0,0"
|
||||
FontSize="18" FontWeight="SemiBold"
|
||||
Foreground="{StaticResource FsTextPrimary}"
|
||||
HorizontalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ui:FluentWindow>
|
||||
1050
src/Everything2Everything.App/Views/MainWindow.xaml.cs
Normal file
1050
src/Everything2Everything.App/Views/MainWindow.xaml.cs
Normal file
File diff suppressed because it is too large
Load diff
40
src/Everything2Everything.App/Views/QuickProgressWindow.xaml
Normal file
40
src/Everything2Everything.App/Views/QuickProgressWindow.xaml
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<ui:FluentWindow x:Class="Everything2Everything.App.Views.QuickProgressWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
Title="JPEG로 빠른 변환"
|
||||
Width="560" Height="220"
|
||||
ExtendsContentIntoTitleBar="True"
|
||||
WindowBackdropType="Mica"
|
||||
WindowCornerPreference="Round"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
ResizeMode="NoResize">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<ui:TitleBar Grid.Row="0" Title="JPEG로 빠른 변환"/>
|
||||
<Grid Grid.Row="1" Margin="32,12,32,24">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Text="변환 중…" Style="{StaticResource TextSubtitle}"/>
|
||||
<TextBlock x:Name="StatusText" Grid.Row="1" Margin="0,4,0,12"
|
||||
Style="{StaticResource TextCaption}" TextTrimming="CharacterEllipsis"/>
|
||||
<ProgressBar x:Name="OverallProgress" Grid.Row="2" Height="6"
|
||||
Minimum="0" Maximum="1"/>
|
||||
<StackPanel Grid.Row="4" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,12,0,0">
|
||||
<ui:Button x:Name="OpenFolderButton" Content="결과 폴더 열기"
|
||||
Icon="{ui:SymbolIcon Folder24}"
|
||||
Click="OnOpenFolderClick" IsEnabled="False" Margin="0,0,8,0"/>
|
||||
<ui:Button x:Name="CloseButton" Content="닫기" Click="OnCloseClick"
|
||||
Appearance="Primary" IsEnabled="False"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ui:FluentWindow>
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
using System.Windows;
|
||||
using Everything2Everything.Core;
|
||||
using Wpf.Ui.Controls;
|
||||
|
||||
namespace Everything2Everything.App.Views;
|
||||
|
||||
public partial class QuickProgressWindow : FluentWindow
|
||||
{
|
||||
private readonly int _total;
|
||||
private string? _firstSuccessOutput;
|
||||
|
||||
public QuickProgressWindow(int total)
|
||||
{
|
||||
_total = total;
|
||||
InitializeComponent();
|
||||
StatusText.Text = $"0 / {_total}";
|
||||
}
|
||||
|
||||
public void Report(ConvertProgress p)
|
||||
{
|
||||
if (!CheckAccess()) { Dispatcher.Invoke(() => Report(p)); return; }
|
||||
var overall = _total == 0 ? 0 : (p.Index + p.FileProgress) / _total;
|
||||
OverallProgress.Value = Math.Clamp(overall, 0, 1);
|
||||
StatusText.Text = $"{Math.Min(p.Index + 1, _total)} / {_total} — {Path.GetFileName(p.CurrentPath)}";
|
||||
}
|
||||
|
||||
public void Finish(IReadOnlyList<ConvertResult> results)
|
||||
{
|
||||
if (!CheckAccess()) { Dispatcher.Invoke(() => Finish(results)); return; }
|
||||
|
||||
var success = results.Count(r => r.Status == ConvertStatus.Success);
|
||||
var skipped = results.Count(r => r.Status == ConvertStatus.Skipped);
|
||||
var failed = results.Count(r => r.Status == ConvertStatus.Failed);
|
||||
var outputs = results.Sum(r => r.OutputPaths.Count);
|
||||
|
||||
OverallProgress.Value = 1;
|
||||
StatusText.Text = $"성공 {success}개 (출력 {outputs}), 건너뜀 {skipped}, 실패 {failed}";
|
||||
CloseButton.IsEnabled = true;
|
||||
|
||||
_firstSuccessOutput = results
|
||||
.FirstOrDefault(r => r.Status == ConvertStatus.Success)?
|
||||
.OutputPaths.FirstOrDefault();
|
||||
OpenFolderButton.IsEnabled = _firstSuccessOutput is not null;
|
||||
|
||||
if (failed > 0)
|
||||
{
|
||||
var detail = string.Join("\n",
|
||||
results.Where(r => r.Status == ConvertStatus.Failed)
|
||||
.Take(5)
|
||||
.Select(r => $"• {Path.GetFileName(r.SourcePath)}: {r.Message}"));
|
||||
MessageBox.Show(this, "일부 파일 변환에 실패했습니다.\n\n" + detail,
|
||||
"Everything2Everything", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
else if (failed == 0 && skipped == 0 && _firstSuccessOutput is not null)
|
||||
{
|
||||
OpenInExplorer(_firstSuccessOutput);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnOpenFolderClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_firstSuccessOutput is not null) OpenInExplorer(_firstSuccessOutput);
|
||||
}
|
||||
|
||||
private void OnCloseClick(object sender, RoutedEventArgs e) => Close();
|
||||
|
||||
private static void OpenInExplorer(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = "explorer.exe",
|
||||
Arguments = $"/select,\"{path}\"",
|
||||
UseShellExecute = true,
|
||||
});
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue