feat(ui): elevate cockpit ergonomics, add search watermark, queue selection styling, and shortcut bindings
This commit is contained in:
parent
4ca7352dc3
commit
388d6707d0
6 changed files with 462 additions and 23 deletions
|
|
@ -26,6 +26,8 @@
|
|||
<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="A" Modifiers="Ctrl" Command="{Binding SelectAllQueueCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<KeyBinding Key="Delete" Command="{Binding DeleteSelectedQueueCommand, 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>
|
||||
|
|
@ -174,7 +176,7 @@
|
|||
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto" Padding="16,16,16,16">
|
||||
<StackPanel>
|
||||
<!-- Smart Conversion Deck: Output Format + Dynamic Presets + Live Spec Chips + Quality -->
|
||||
<Border Style="{StaticResource FsDoubleBezelShellStyle}" Margin="0,0,0,16">
|
||||
<Border Style="{StaticResource FsDoubleBezelShellStyle}" Margin="0,0,0,12">
|
||||
<Border Style="{StaticResource FsDoubleBezelCoreStyle}">
|
||||
<StackPanel>
|
||||
<Grid Margin="0,0,0,12">
|
||||
|
|
@ -217,6 +219,7 @@
|
|||
</Grid>
|
||||
<TextBlock x:Name="OutputFormatHint" Margin="0,0,0,12"
|
||||
Style="{StaticResource FsCaptionStyle}"
|
||||
TextWrapping="Wrap"
|
||||
Text="모든 입력에서 변환 가능한 형식이 표시됩니다"/>
|
||||
|
||||
<Border Height="1" Background="{StaticResource FsBorderSubtle}" Margin="0,0,0,12"/>
|
||||
|
|
@ -464,16 +467,16 @@
|
|||
</Border>
|
||||
|
||||
<!-- Card 3: Output Destination & Conflict Rule -->
|
||||
<Border Style="{StaticResource FsDoubleBezelShellStyle}" Margin="0,0,0,16">
|
||||
<Border Style="{StaticResource FsDoubleBezelShellStyle}" Margin="0,0,0,12">
|
||||
<Border Style="{StaticResource FsDoubleBezelCoreStyle}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="저장 위치 및 충돌 규칙" Style="{StaticResource FsLabelStyle}"/>
|
||||
<Grid Margin="0,10,0,0">
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox x:Name="OutputPathTextBox"
|
||||
<TextBox x:Name="OutputPathTextBox"
|
||||
Style="{StaticResource FsPathInputStyle}"
|
||||
Text="{Binding Options.CustomOutputDirectory, RelativeSource={RelativeSource AncestorType=Window}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
ToolTip="비워두면 원본 폴더 옆 서브폴더에 저장"/>
|
||||
|
|
@ -481,11 +484,11 @@
|
|||
Content="…" Style="{StaticResource FsSecondaryButtonStyle}"
|
||||
Command="{Binding PickOutputFolderCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
</Grid>
|
||||
<TextBlock x:Name="OutputDestHint" Margin="0,6,0,0"
|
||||
<TextBlock x:Name="OutputDestHint" Margin="0,5,0,0"
|
||||
Style="{StaticResource FsCaptionStyle}"
|
||||
Text="비워두면 원본 옆 _jpg 폴더에 저장됩니다"/>
|
||||
|
||||
<Border Height="1" Background="{StaticResource FsBorderSubtle}" Margin="0,14,0,14"/>
|
||||
<Border Height="1" Background="{StaticResource FsBorderSubtle}" Margin="0,8,0,8"/>
|
||||
|
||||
<TextBlock Text="파일 충돌 해결" Style="{StaticResource FsLabelStyle}"/>
|
||||
<Border Margin="0,8,0,0"
|
||||
|
|
@ -592,7 +595,7 @@
|
|||
|
||||
<!-- Sidebar footer -->
|
||||
<Border Grid.Row="1" BorderBrush="{StaticResource FsBorderSubtle}"
|
||||
BorderThickness="0,1,0,0" Padding="24">
|
||||
BorderThickness="0,1,0,0" Padding="16,12">
|
||||
<StackPanel>
|
||||
<TextBlock x:Name="CapabilityStatusText" Margin="0,0,0,12"
|
||||
Style="{StaticResource FsCaptionStyle}"
|
||||
|
|
@ -666,16 +669,56 @@
|
|||
BorderThickness="0,0,1,1" Padding="16,8">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="160"/>
|
||||
<ColumnDefinition Width="175"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<!-- Search Box -->
|
||||
<Grid Grid.Column="0">
|
||||
<!-- Search Box with Live Watermark & Clear Button -->
|
||||
<Grid Grid.Column="0" Width="175" VerticalAlignment="Center">
|
||||
<TextBox x:Name="SearchBox" Style="{StaticResource FsPathInputStyle}"
|
||||
Padding="30,5,8,5" Text="{Binding SearchText, RelativeSource={RelativeSource AncestorType=Window}, UpdateSourceTrigger=PropertyChanged}"
|
||||
ToolTip="파일명 또는 확장자로 검색"/>
|
||||
Padding="28,5,24,5" Text="{Binding SearchText, RelativeSource={RelativeSource AncestorType=Window}, UpdateSourceTrigger=PropertyChanged}"
|
||||
ToolTip="파일명 또는 확장자로 검색" VerticalAlignment="Center"/>
|
||||
<ui:SymbolIcon Symbol="Search24" FontSize="13" Foreground="{StaticResource FsTextTertiary}"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Center" Margin="9,0,0,0" IsHitTestVisible="False"/>
|
||||
HorizontalAlignment="Left" VerticalAlignment="Center" Margin="8,0,0,0" IsHitTestVisible="False"/>
|
||||
<TextBlock x:Name="SearchPlaceholderText"
|
||||
Text="파일명 검색…"
|
||||
Foreground="{StaticResource FsTextTertiary}"
|
||||
VerticalAlignment="Center" Margin="28,0,0,0"
|
||||
IsHitTestVisible="False">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock" BasedOn="{StaticResource FsCaptionStyle}">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Text, ElementName=SearchBox}" Value="">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Text, ElementName=SearchBox}" Value="{x:Null}">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
<Button x:Name="SearchClearButton"
|
||||
Width="20" Height="20"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||
Margin="0,0,6,0"
|
||||
ToolTip="검색어 지우기"
|
||||
Command="{Binding ClearSearchCommand, RelativeSource={RelativeSource AncestorType=Window}}">
|
||||
<Button.Style>
|
||||
<Style TargetType="Button" BasedOn="{StaticResource FsIconButtonStyle}">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Text, ElementName=SearchBox}" Value="">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Text, ElementName=SearchBox}" Value="{x:Null}">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
<ui:SymbolIcon Symbol="Dismiss24" FontSize="10" Foreground="{StaticResource FsTextTertiary}" VerticalAlignment="Center"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
<!-- Category Filter Chips & Inspector Toggle -->
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
|
|
@ -751,6 +794,28 @@
|
|||
<CheckBox x:Name="BatchSelectAllCheck" Content="전체 선택" VerticalAlignment="Center"
|
||||
Command="{Binding BatchSelectAllCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
CommandParameter="{Binding IsChecked, RelativeSource={RelativeSource Self}}"/>
|
||||
<!-- Live Queue Telemetry Capsule (Cockpit Summary) -->
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Border Background="#10FFFFFF" BorderBrush="{StaticResource FsBorderHairline}"
|
||||
BorderThickness="1" CornerRadius="12" Padding="10,3" VerticalAlignment="Center">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<TextBlock x:Name="QueueSummaryCountText" Text="0개 항목"
|
||||
FontFamily="{StaticResource FsFontSans}" FontSize="11" FontWeight="SemiBold"
|
||||
Foreground="{StaticResource FsTextPrimary}" VerticalAlignment="Center"/>
|
||||
<TextBlock Text=" · " FontSize="11" Foreground="{StaticResource FsTextTertiary}" VerticalAlignment="Center"/>
|
||||
<TextBlock x:Name="QueueSummarySizeText" Text="0 B"
|
||||
FontFamily="{StaticResource FsFontMono}" FontSize="11"
|
||||
Foreground="{StaticResource FsAccentCyan}" VerticalAlignment="Center"/>
|
||||
<Border x:Name="QueueSelectedBadge" Visibility="Collapsed"
|
||||
Margin="8,0,0,0" Background="{StaticResource FsAccentCyanBg}"
|
||||
CornerRadius="6" Padding="6,1" VerticalAlignment="Center">
|
||||
<TextBlock x:Name="QueueSelectedText" Text="0개 선택됨"
|
||||
FontSize="10" FontWeight="SemiBold"
|
||||
Foreground="{StaticResource FsAccentCyan}" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<Button Margin="0,0,8,0" Padding="10,4" Style="{StaticResource FsSecondaryButtonStyle}"
|
||||
Command="{Binding BatchRemoveSelectedCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
|
|
@ -843,9 +908,18 @@
|
|||
ItemsSource="{Binding ActiveQueue, RelativeSource={RelativeSource AncestorType=Window}}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<!-- row hover: FsListRowStyle Neo Glass 카드 + 좌측 형식색 accent bar -->
|
||||
<Border x:Name="QueueRow" Style="{StaticResource FsListRowStyle}" Padding="0"
|
||||
Cursor="Hand">
|
||||
<!-- row hover: FsListRowStyle Neo Glass 카드 + 좌측 형식색 accent bar + IsSelected 강조 -->
|
||||
<Border x:Name="QueueRow" Padding="0" Cursor="Hand">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border" BasedOn="{StaticResource FsListRowStyle}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsSelected}" Value="True">
|
||||
<Setter Property="BorderBrush" Value="#6038BDF8"/>
|
||||
<Setter Property="Background" Value="#162033"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
<i:Interaction.Triggers>
|
||||
<i:EventTrigger EventName="PreviewMouseLeftButtonUp">
|
||||
<i:InvokeCommandAction Command="{Binding QueueRowCommand, RelativeSource={RelativeSource AncestorType=Window}}" PassEventArgsToCommand="True"/>
|
||||
|
|
@ -939,6 +1013,52 @@
|
|||
|
||||
<!-- Past Results view -->
|
||||
<Grid x:Name="PastResultsContainer" Visibility="Collapsed">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Telemetry Header Bar -->
|
||||
<Border x:Name="PastResultsHeaderBar" Grid.Row="0"
|
||||
Background="{StaticResource FsBgPanel}"
|
||||
BorderBrush="{StaticResource FsBorderSubtle}"
|
||||
BorderThickness="0,0,0,1" Padding="24,8">
|
||||
<Grid VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border Background="#10FFFFFF" BorderBrush="{StaticResource FsBorderHairline}"
|
||||
BorderThickness="1" CornerRadius="12" Padding="10,4" VerticalAlignment="Center">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<ui:SymbolIcon Symbol="CheckmarkCircle24" FontSize="13" Foreground="{StaticResource FsAccentGreen}"
|
||||
VerticalAlignment="Center" Margin="0,0,6,0"/>
|
||||
<TextBlock Text="누적 변환: " Style="{StaticResource FsCaptionStyle}" VerticalAlignment="Center"/>
|
||||
<TextBlock x:Name="PastTotalCountText" Text="0개 파일"
|
||||
FontFamily="{StaticResource FsFontMono}" FontSize="11" FontWeight="SemiBold"
|
||||
Foreground="{StaticResource FsTextPrimary}" VerticalAlignment="Center"/>
|
||||
<TextBlock Text=" · " FontSize="11" Foreground="{StaticResource FsTextTertiary}" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="총 절감: " Style="{StaticResource FsCaptionStyle}" VerticalAlignment="Center"/>
|
||||
<TextBlock x:Name="PastTotalSavingsText" Text="0 B"
|
||||
FontFamily="{StaticResource FsFontMono}" FontSize="11" FontWeight="SemiBold"
|
||||
Foreground="{StaticResource FsAccentGreen}" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<Button Padding="10,4" Style="{StaticResource FsSecondaryButtonStyle}"
|
||||
Command="{Binding ExportLogCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
ToolTip="변환 기록 CSV/JSON 내보내기">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<ui:SymbolIcon Symbol="ArrowDownload24" FontSize="12" VerticalAlignment="Center" Margin="0,0,4,0"/>
|
||||
<TextBlock Text="내보내기" FontSize="11" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<!-- 빈 상태 안내 (Empty State) -->
|
||||
<Grid x:Name="PastResultsEmpty" Visibility="Collapsed">
|
||||
<Border Background="{StaticResource FsBgBase}" Padding="24">
|
||||
|
|
@ -1106,6 +1226,7 @@
|
|||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
|
@ -1211,6 +1332,60 @@
|
|||
BorderBrush="{StaticResource FsBorderSubtle}"
|
||||
BorderThickness="0,1,0,0">
|
||||
<StackPanel>
|
||||
<!-- Conversion Pipeline Card (원본 -> 변환 대상 예상) -->
|
||||
<Border x:Name="ConversionPipelineCard"
|
||||
Margin="0,0,0,12"
|
||||
Background="{StaticResource FsBgSurface}"
|
||||
BorderBrush="{StaticResource FsBorderHairline}"
|
||||
BorderThickness="1" CornerRadius="8" Padding="12,10">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||
<TextBlock Text="현재 파일" Style="{StaticResource FsCaptionStyle}" FontSize="10" VerticalAlignment="Center"/>
|
||||
<TextBlock x:Name="PipelineSourceText" Text="—"
|
||||
FontFamily="{StaticResource FsFontMono}" FontSize="12"
|
||||
FontWeight="SemiBold" Foreground="{StaticResource FsTextPrimary}"
|
||||
Margin="0,2,0,0" TextTrimming="CharacterEllipsis" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<ui:SymbolIcon Grid.Column="1" Symbol="ArrowRight24" FontSize="14"
|
||||
Foreground="{StaticResource FsAccentCyan}"
|
||||
VerticalAlignment="Center" Margin="8,0"/>
|
||||
<StackPanel Grid.Column="2" VerticalAlignment="Center" HorizontalAlignment="Right">
|
||||
<TextBlock Text="변환 대상" Style="{StaticResource FsCaptionStyle}" FontSize="10" HorizontalAlignment="Right" VerticalAlignment="Center"/>
|
||||
<TextBlock x:Name="PipelineTargetText" Text="—"
|
||||
FontFamily="{StaticResource FsFontMono}" FontSize="12"
|
||||
FontWeight="SemiBold" Foreground="{StaticResource FsAccentCyan}"
|
||||
Margin="0,2,0,0" HorizontalAlignment="Right" TextTrimming="CharacterEllipsis" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Border Grid.Row="1" Margin="0,8,0,0" Background="#0CFFFFFF"
|
||||
CornerRadius="4" Padding="8,4" VerticalAlignment="Center">
|
||||
<Grid VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Name="PipelineLossText" Text="최적화 변환 준비"
|
||||
FontSize="11" Foreground="{StaticResource FsAccentGreen}"
|
||||
VerticalAlignment="Center"/>
|
||||
<TextBlock x:Name="PipelineEstimatedText" Grid.Column="1"
|
||||
Text="실시간 엔진 매칭"
|
||||
FontFamily="{StaticResource FsFontMono}" FontSize="11"
|
||||
Foreground="{StaticResource FsAccentGreen}" VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<TextBlock x:Name="PreviewFileName"
|
||||
Style="{StaticResource FsBodyStyle}"
|
||||
FontWeight="SemiBold" TextWrapping="NoWrap"
|
||||
|
|
@ -1268,9 +1443,11 @@
|
|||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Grid.Column="0"
|
||||
<Button x:Name="PreviewOpenButton"
|
||||
Grid.Column="0"
|
||||
Style="{StaticResource FsSecondaryButtonStyle}"
|
||||
Padding="12,8"
|
||||
IsEnabled="{Binding HasSelectedItem, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
Command="{Binding PreviewOpenFileCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
ToolTip="기본 프로그램으로 파일 열기">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
|
|
@ -1278,9 +1455,11 @@
|
|||
<TextBlock Text="열기" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Grid.Column="2"
|
||||
<Button x:Name="PreviewRevealButton"
|
||||
Grid.Column="2"
|
||||
Style="{StaticResource FsSecondaryButtonStyle}"
|
||||
Padding="12,8"
|
||||
IsEnabled="{Binding HasSelectedItem, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
Command="{Binding PreviewOpenFolderCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
ToolTip="탐색기에서 파일 위치 열기">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
|
|
|
|||
|
|
@ -18,8 +18,30 @@ using LossClass = Everything2Everything.Core.Providers.LossClass;
|
|||
|
||||
namespace Everything2Everything.App.Views;
|
||||
|
||||
public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
||||
public partial class MainWindow : Wpf.Ui.Controls.FluentWindow, INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
private void OnPropertyChanged([CallerMemberName] string? name = null)
|
||||
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
||||
|
||||
private bool _hasSelectedItem;
|
||||
public bool HasSelectedItem
|
||||
{
|
||||
get => _hasSelectedItem;
|
||||
set
|
||||
{
|
||||
if (_hasSelectedItem != value)
|
||||
{
|
||||
_hasSelectedItem = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand ClearSearchCommand { get; }
|
||||
public ICommand SelectAllQueueCommand => BatchSelectAllCommand;
|
||||
public ICommand DeleteSelectedQueueCommand => BatchRemoveSelectedCommand;
|
||||
|
||||
private readonly ConversionEngine _engine;
|
||||
private readonly ISettingsStore _settings;
|
||||
private readonly OptionsViewModel _options = new();
|
||||
|
|
@ -153,6 +175,11 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
DragLeaveCommand = new RelayCommand(_ => DropHintOverlay.Visibility = Visibility.Collapsed);
|
||||
DropCommand = new RelayCommand(p => HandleFilesDropped(p as DragEventArgs));
|
||||
QueueRowCommand = new RelayCommand(p => HandleQueueRowClick(p as MouseButtonEventArgs));
|
||||
ClearSearchCommand = new RelayCommand(_ =>
|
||||
{
|
||||
SearchText = "";
|
||||
if (SearchBox is not null) SearchBox.Text = "";
|
||||
});
|
||||
PastRowCommand = new RelayCommand(p => HandlePastRowClick(p as MouseButtonEventArgs));
|
||||
|
||||
InitializeComponent();
|
||||
|
|
@ -302,7 +329,9 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
foreach (var path in paths)
|
||||
{
|
||||
if (!File.Exists(path) || existing.Contains(path)) continue;
|
||||
_activeQueue.Add(QueueItem.FromPath(path));
|
||||
var item = QueueItem.FromPath(path);
|
||||
item.PropertyChanged += OnQueueItemPropertyChanged;
|
||||
_activeQueue.Add(item);
|
||||
}
|
||||
UpdateBadges();
|
||||
UpdateProcessQueueButton();
|
||||
|
|
@ -316,9 +345,18 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
}
|
||||
}
|
||||
|
||||
private void OnQueueItemPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(QueueItem.IsSelected))
|
||||
{
|
||||
UpdateQueueSummary();
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveQueueItem(QueueItem? item)
|
||||
{
|
||||
if (item is null) return;
|
||||
item.PropertyChanged -= OnQueueItemPropertyChanged;
|
||||
_activeQueue.Remove(item);
|
||||
UpdateBadges();
|
||||
UpdateProcessQueueButton();
|
||||
|
|
@ -350,6 +388,40 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
var count = _pastResults.Sum(g => g.Entries.Count);
|
||||
TabPastBadge.Text = count.ToString(CultureInfo.InvariantCulture);
|
||||
UpdatePastResultsVisibility();
|
||||
UpdateQueueSummary();
|
||||
UpdatePastResultsTelemetry();
|
||||
}
|
||||
|
||||
private void UpdateQueueSummary()
|
||||
{
|
||||
if (QueueSummaryCountText is null || QueueSummarySizeText is null) return;
|
||||
var count = _activeQueue.Count;
|
||||
long totalBytes = _activeQueue.Sum(q => q.SourceSizeBytes);
|
||||
QueueSummaryCountText.Text = $"총 {count}개 항목";
|
||||
QueueSummarySizeText.Text = HumanizeBytes(totalBytes);
|
||||
|
||||
var selectedCount = _activeQueue.Count(q => q.IsSelected);
|
||||
if (QueueSelectedBadge is not null && QueueSelectedText is not null)
|
||||
{
|
||||
if (selectedCount > 0)
|
||||
{
|
||||
QueueSelectedBadge.Visibility = Visibility.Visible;
|
||||
QueueSelectedText.Text = $"{selectedCount}개 선택됨";
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueSelectedBadge.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePastResultsTelemetry()
|
||||
{
|
||||
if (PastTotalCountText is null || PastTotalSavingsText is null) return;
|
||||
var totalCount = _pastResults.Sum(g => g.Entries.Count);
|
||||
var totalSavings = _pastResults.Sum(g => g.SessionSavingsBytes);
|
||||
PastTotalCountText.Text = $"{totalCount}개 파일";
|
||||
PastTotalSavingsText.Text = HumanizeBytes(totalSavings);
|
||||
}
|
||||
|
||||
private void UpdateProcessQueueButton()
|
||||
|
|
@ -523,10 +595,44 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
var info = FileInspectorBuilder.Build(filePath);
|
||||
PreviewFileName.Text = string.IsNullOrEmpty(fileName) ? info.FileName : fileName;
|
||||
PreviewFilePath.Text = string.IsNullOrEmpty(filePath) ? info.FullPath : filePath;
|
||||
PreviewFormatText.Text = string.IsNullOrEmpty(formatLabel) || formatLabel == "—" ? info.Extension.TrimStart('.').ToUpperInvariant() : formatLabel;
|
||||
PreviewSizeText.Text = string.IsNullOrEmpty(sizeText) || sizeText == "—" ? info.FormattedSize : sizeText;
|
||||
var fmt = string.IsNullOrEmpty(formatLabel) || formatLabel == "—" ? info.Extension.TrimStart('.').ToUpperInvariant() : formatLabel;
|
||||
var sz = string.IsNullOrEmpty(sizeText) || sizeText == "—" ? info.FormattedSize : sizeText;
|
||||
PreviewFormatText.Text = fmt;
|
||||
PreviewSizeText.Text = sz;
|
||||
PreviewDimText.Text = info.DimensionsOrMeta;
|
||||
PreviewPageText.Text = info.Category.ToKoreanLabel();
|
||||
UpdateConversionPipelineCard(fmt, sz);
|
||||
HasSelectedItem = !string.IsNullOrEmpty(filePath);
|
||||
}
|
||||
|
||||
private void UpdateConversionPipelineCard(string? formatLabel, string? sizeText)
|
||||
{
|
||||
if (ConversionPipelineCard is null || PipelineSourceText is null || PipelineTargetText is null) return;
|
||||
|
||||
if (string.IsNullOrEmpty(formatLabel) || formatLabel == "—")
|
||||
{
|
||||
PipelineSourceText.Text = "선택 대기";
|
||||
PipelineTargetText.Text = (SelectedOutputExtension ?? ".jpg").TrimStart('.').ToUpperInvariant();
|
||||
if (PipelineLossText is not null) PipelineLossText.Text = "파일을 선택하면 변환 정보 표시";
|
||||
if (PipelineEstimatedText is not null) PipelineEstimatedText.Text = "대기 중";
|
||||
return;
|
||||
}
|
||||
|
||||
PipelineSourceText.Text = $"{formatLabel} · {sizeText ?? "—"}";
|
||||
var targetExt = (SelectedOutputExtension ?? ".jpg").TrimStart('.').ToUpperInvariant();
|
||||
PipelineTargetText.Text = targetExt;
|
||||
|
||||
var isSame = string.Equals(formatLabel, targetExt, StringComparison.OrdinalIgnoreCase);
|
||||
if (isSame)
|
||||
{
|
||||
if (PipelineLossText is not null) PipelineLossText.Text = "무손실 재압축 및 메타 정돈";
|
||||
if (PipelineEstimatedText is not null) PipelineEstimatedText.Text = "최적화";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PipelineLossText is not null) PipelineLossText.Text = $"{formatLabel} → {targetExt} 변환";
|
||||
if (PipelineEstimatedText is not null) PipelineEstimatedText.Text = "실시간 매칭";
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowPreviewLoading()
|
||||
|
|
@ -751,6 +857,7 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
{
|
||||
bool select = parameter is true;
|
||||
BatchQueueService.SetSelectionAll(_activeQueue, select);
|
||||
UpdateQueueSummary();
|
||||
}
|
||||
|
||||
private void BatchRemoveSelected()
|
||||
|
|
@ -1334,6 +1441,7 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
UpdateCombineState(keepExt);
|
||||
UpdateProcessQueueButton();
|
||||
UpdateSmartPresetsForFormat(keepExt);
|
||||
UpdateConversionPipelineCard(PreviewFormatText?.Text, PreviewSizeText?.Text);
|
||||
|
||||
if (OutputFormatHint is not null)
|
||||
{
|
||||
|
|
@ -1379,6 +1487,7 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
UpdateQualityPanelForFormat(ext);
|
||||
UpdateOutputDestHint(ext);
|
||||
UpdateSmartPresetsForFormat(ext);
|
||||
UpdateConversionPipelineCard(PreviewFormatText?.Text, PreviewSizeText?.Text);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue