releases/VideoDownloader.App/MainWindow.xaml

399 lines
28 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<Window x:Class="VideoDownloader.App.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
xmlns:vm="clr-namespace:VideoDownloader.App.ViewModels"
mc:Ignorable="d"
FontFamily="{StaticResource UiFont}"
Title="Video Downloader" Height="820" Width="1300"
Background="{StaticResource WindowBgGradient}"
WindowStartupLocation="CenterScreen"
WindowStyle="None" ResizeMode="CanResize">
<!-- 보더리스: WindowChrome 이 실제 엣지 리사이즈 + 올바른 최대화(작업표시줄 존중) + DWM 그림자 담당 -->
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0"
ResizeBorderThickness="6"
GlassFrameThickness="1"
CornerRadius="0"
UseAeroCaptionButtons="False"
NonClientFrameEdges="None"/>
</WindowChrome.WindowChrome>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
<DataTemplate DataType="{x:Type vm:DownloadItemViewModel}">
<Border Background="{StaticResource CardBg}" BorderBrush="{StaticResource BorderSubtle}"
BorderThickness="1" CornerRadius="12" Padding="13" Margin="0,0,0,10">
<StackPanel>
<DockPanel>
<Border DockPanel.Dock="Right" Background="{StaticResource GlassBg}"
BorderBrush="{StaticResource BorderBrush}" BorderThickness="1"
CornerRadius="6" Padding="8,2" VerticalAlignment="Center">
<TextBlock Text="{Binding FormatTag}" FontSize="10" FontWeight="Bold"
Foreground="{StaticResource AccentBrush}"/>
</Border>
<TextBlock Text="{Binding Title}" Foreground="{StaticResource TextPrimary}"
FontSize="13" FontWeight="SemiBold" TextTrimming="CharacterEllipsis"
VerticalAlignment="Center"/>
</DockPanel>
<!-- 소셜(YouTube/Instagram/X) 카드: 화질 선택 -->
<DockPanel Margin="0,9,0,2" Visibility="{Binding IsSocial, Converter={StaticResource BoolToVis}}">
<Button DockPanel.Dock="Right" Click="CardFetchFormats_Click" Margin="7,0,0,0"
VerticalAlignment="Center" ToolTip="이 영상에서 사용 가능한 화질 조회">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource CardActionButton}">
<Setter Property="Content" Value="불러오기"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsFetchingFormats}" Value="True">
<Setter Property="Content" Value="…"/>
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="다운로드 중"><Setter Property="IsEnabled" Value="False"/></DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="병합 중"><Setter Property="IsEnabled" Value="False"/></DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="리먹스 중"><Setter Property="IsEnabled" Value="False"/></DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="완료"><Setter Property="IsEnabled" Value="False"/></DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<ComboBox Style="{StaticResource DarkComboBox}"
ItemsSource="{Binding Formats}" DisplayMemberPath="Label"
SelectedItem="{Binding SelectedFormat}"
VerticalAlignment="Center"/>
</DockPanel>
<DockPanel Margin="0,11,0,6">
<TextBlock DockPanel.Dock="Right" Text="{Binding Segments}"
Foreground="{StaticResource TextMuted}" FontSize="11"
VerticalAlignment="Center" Margin="8,0,0,0"/>
<Grid VerticalAlignment="Center">
<ProgressBar Value="{Binding Progress}" Maximum="100">
<ProgressBar.Style>
<Style TargetType="ProgressBar" BasedOn="{StaticResource AccentProgressBar}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsIndeterminate}" Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ProgressBar.Style>
</ProgressBar>
<ProgressBar>
<ProgressBar.Style>
<Style TargetType="ProgressBar" BasedOn="{StaticResource IndeterminateBar}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsIndeterminate}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ProgressBar.Style>
</ProgressBar>
</Grid>
</DockPanel>
<DockPanel Margin="0,4,0,0">
<TextBlock Text="{Binding Status}" FontSize="11" VerticalAlignment="Center">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource TextSecondary}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Status}" Value="오류">
<Setter Property="Foreground" Value="{StaticResource ErrorBrush}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="완료">
<Setter Property="Foreground" Value="{StaticResource SuccessBrush}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
<Button Style="{StaticResource CardActionButton}" Content="받기" Click="CardDownload_Click" Margin="0,0,6,0"/>
<Button Content="취소" Click="CardCancel_Click" Margin="0,0,6,0">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource CardActionButton}">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Status}" Value="다운로드 중"><Setter Property="IsEnabled" Value="True"/></DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="병합 중"><Setter Property="IsEnabled" Value="True"/></DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="리먹스 중"><Setter Property="IsEnabled" Value="True"/></DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Style="{StaticResource CardActionButton}" Content="재시도" Click="CardRetry_Click" Margin="0,0,6,0"/>
<Button Style="{StaticResource CardActionButton}" Content="폴더" Click="CardFolder_Click"/>
</StackPanel>
</DockPanel>
<StackPanel Orientation="Horizontal" Margin="0,8,0,0">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Status}" Value="오류">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<TextBlock Text="&#xE7BA;" Style="{StaticResource FluentIcon}" Foreground="{StaticResource ErrorBrush}" FontSize="13" Margin="0,0,6,0"/>
<TextBlock Text="{Binding Error}" Foreground="{StaticResource ErrorBrush}" FontSize="11" TextWrapping="Wrap" MaxWidth="300"/>
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- ===== 상단 툴바 (슬림, 전체가 드래그 핸들) ===== -->
<Border Grid.Row="0" Background="{StaticResource PanelBg}"
BorderBrush="{StaticResource AccentFadeBrush}" BorderThickness="0,0,0,2" Padding="20,9,0,9"
PreviewMouseLeftButtonDown="TitleBar_PreviewMouseLeftButtonDown">
<DockPanel>
<!-- 브랜드 -->
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal"
VerticalAlignment="Center" Margin="0,0,12,0">
<Border Width="26" Height="26" CornerRadius="7" Background="{StaticResource AccentGradient}">
<TextBlock Text="&#xE896;" Style="{StaticResource FluentIcon}" Foreground="White" FontSize="14"/>
</Border>
<TextBlock Text="Video Downloader" Foreground="{StaticResource TextPrimary}"
FontWeight="SemiBold" FontSize="13.5" VerticalAlignment="Center" Margin="9,0,0,0"/>
</StackPanel>
<!-- 네비게이션 (Fluent 아이콘) -->
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal" VerticalAlignment="Center">
<Button Name="NavBack" Style="{StaticResource IconButton}" Content="&#xE76B;" ToolTip="뒤로 (Alt+←)" Margin="0,0,3,0" Click="NavBack_Click"/>
<Button Name="NavForward" Style="{StaticResource IconButton}" Content="&#xE76C;" ToolTip="앞으로 (Alt+→)" Margin="0,0,3,0" Click="NavForward_Click"/>
<Button Name="NavRefresh" Style="{StaticResource IconButton}" Content="&#xE72C;" ToolTip="새로고침 (F5)" Margin="0,0,3,0" Click="NavRefresh_Click"/>
<Button Name="NavHome" Style="{StaticResource IconButton}" Content="&#xE80F;" ToolTip="홈" Click="NavHome_Click"/>
</StackPanel>
<!-- 우측 액션 + 캡션 (Fluent 아이콘 + 툴팁) -->
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" VerticalAlignment="Center" Margin="10,0,0,0">
<Button Name="BookmarksBtn" Style="{StaticResource IconButton}" Content="&#xE734;" ToolTip="즐겨찾기" Margin="0,0,2,0" Click="ToggleBookmarks"/>
<Button Name="HistoryBtn" Style="{StaticResource IconButton}" Content="&#xE823;" ToolTip="방문 기록" Margin="0,0,2,0" Click="ToggleHistory"/>
<Button Style="{StaticResource IconButton}" Content="&#xE8B7;" ToolTip="저장 폴더 열기" Margin="0,0,2,0" Click="OpenFolder_Click"/>
<Button Name="LinkBtn" Style="{StaticResource IconButton}" Content="&#xE71B;" ToolTip="링크로 받기 (YouTube·Instagram·X)" Margin="0,0,2,0" Click="ToggleLink"/>
<Button x:Name="PrivateBtn" Style="{StaticResource IconButton}" Content="&#xE72E;" ToolTip="개인정보 보호 모드(InPrivate)" Margin="0,0,2,0" Click="TogglePrivate_Click"/>
<Button Style="{StaticResource IconButton}" Content="&#xE713;" ToolTip="설정" Margin="0,0,8,0" Click="Settings_Click"/>
<Button Style="{StaticResource PrimaryButton}" Content="이동" ToolTip="주소로 이동 (Enter)" Height="32" Click="Go_Click"/>
<!-- 구분선 (좌우 대칭 여백) -->
<Border Width="1" Background="{StaticResource BorderSubtle}" Margin="10,8"/>
<!-- 윈도우 캡션 컨트롤 (고정폭, 서로 밀착) -->
<Button Style="{StaticResource CaptionButton}" Content="&#xE921;" ToolTip="최소화" Click="Minimize_Click"/>
<Button Name="MaxRestoreBtn" Style="{StaticResource CaptionButton}" Content="&#xE922;" ToolTip="최대화" Click="MaxRestore_Click"/>
<Button Style="{StaticResource CloseButton}" Content="&#xE8BB;" ToolTip="닫기" Click="Close_Click"/>
</StackPanel>
<!-- 주소창 -->
<Border Background="{StaticResource GlassBg}" BorderBrush="{StaticResource BorderBrush}"
BorderThickness="1" CornerRadius="8" Height="32" VerticalAlignment="Center">
<TextBox Name="AddressBox" Style="{StaticResource DarkTextBox}"
PreviewKeyDown="AddressBox_PreviewKeyDown" TextChanged="AddressBox_TextChanged"
BorderThickness="0" Background="Transparent"
Padding="14,0" FontSize="13" VerticalContentAlignment="Center"/>
</Border>
</DockPanel>
</Border>
<!-- ===== 본문 ===== -->
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition x:Name="RightCol" Width="420" MinWidth="56" MaxWidth="900"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" ClipToBounds="True" Background="Black">
<wv2:WebView2 Name="Browser"/>
</Border>
<!-- 리사이즈 핸들 (펼침 상태에서만 노출). 드래그로 우측 패널 폭 변경. -->
<GridSplitter x:Name="RightSplitter" Grid.Column="1" Width="7"
HorizontalAlignment="Center" VerticalAlignment="Stretch"
ResizeBehavior="PreviousAndNext" ResizeDirection="Columns"
Background="Transparent" Cursor="SizeWE"
DragCompleted="RightSplitter_DragCompleted">
<GridSplitter.Template>
<ControlTemplate TargetType="GridSplitter">
<Border x:Name="handle" Background="Transparent">
<Rectangle Width="1" HorizontalAlignment="Center" Fill="{StaticResource BorderSubtle}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="handle" Property="Background" Value="{StaticResource AccentFadeBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</GridSplitter.Template>
</GridSplitter>
<!-- 범용 우측 패널: 헤더(토글 버튼, 항상 노출) + 본문(펼침 시). 접기/펼치기·폭·상태 저장. -->
<Border Grid.Column="2" Background="{StaticResource PanelBg}"
BorderBrush="{StaticResource BorderSubtle}" BorderThickness="1,0,0,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Margin="10,12,10,8" LastChildFill="True">
<Button x:Name="PanelToggle" DockPanel.Dock="Right" Style="{StaticResource IconButton}"
Click="PanelToggle_Click" ToolTip="패널 접기"
VerticalAlignment="Center" Margin="4,0,0,0" Content="&#xE76C;"/>
<Border x:Name="PanelCountBadge" DockPanel.Dock="Right" Background="{StaticResource GlassBg}"
CornerRadius="9" Padding="9,2" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,8,0">
<TextBlock Text="{Binding Items.Count}" Foreground="{StaticResource TextSecondary}" FontSize="11"/>
</Border>
<TextBlock x:Name="PanelTitle" Text="다운로드" Foreground="{StaticResource TextPrimary}"
FontWeight="SemiBold" FontSize="13.5" VerticalAlignment="Center"/>
</DockPanel>
<Grid Grid.Row="1" x:Name="PanelBody">
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0,60,0,0"
Visibility="{Binding Items.Count, Converter={StaticResource InverseCountToVisibility}}">
<Border Width="54" Height="54" CornerRadius="14" Background="{StaticResource GlassBg}"
BorderBrush="{StaticResource BorderBrush}" BorderThickness="1" HorizontalAlignment="Center">
<TextBlock Text="&#xE8B2;" Style="{StaticResource FluentIcon}" FontSize="24" Foreground="{StaticResource TextMuted}"/>
</Border>
<TextBlock Text="영상을 재생하면 여기에 나타납니다" Foreground="{StaticResource TextSecondary}"
FontSize="13" FontWeight="Medium" HorizontalAlignment="Center" Margin="0,14,0,2"/>
<TextBlock Text="탐지 즉시 자동으로 목록에 추가돼요" Foreground="{StaticResource TextMuted}"
FontSize="11.5" HorizontalAlignment="Center"/>
</StackPanel>
<ScrollViewer VerticalScrollBarVisibility="Auto" Padding="16,2,16,16">
<ItemsControl ItemsSource="{Binding Items}"/>
</ScrollViewer>
</Grid>
</Grid>
</Border>
</Grid>
<!-- ===== 상태 표시줄 ===== -->
<Border Grid.Row="2" Background="{StaticResource PanelBg}"
BorderBrush="{StaticResource BorderSubtle}" BorderThickness="0,1,0,0" Padding="16,5">
<TextBlock Name="StatusText" Foreground="{StaticResource TextMuted}" FontSize="11"
TextTrimming="CharacterEllipsis" Text="준비"/>
</Border>
<!-- ===== 즐겨찾기 플라이아웃 ===== -->
<Popup Name="BookmarksPopup" PlacementTarget="{Binding ElementName=BookmarksBtn}"
Placement="Bottom" HorizontalOffset="-10" VerticalOffset="6"
StaysOpen="False" AllowsTransparency="True" PopupAnimation="Fade">
<Border Background="{StaticResource CardBg}" BorderBrush="{StaticResource BorderBrush}"
BorderThickness="1" CornerRadius="12" Padding="8" Width="320" MaxHeight="460">
<StackPanel>
<DockPanel Margin="6,4,6,6">
<TextBlock Text="즐겨찾기" Foreground="{StaticResource TextPrimary}" FontWeight="SemiBold" VerticalAlignment="Center"/>
<Button DockPanel.Dock="Right" Style="{StaticResource CardActionButton}" Content="+ 현재 페이지 추가" Click="AddBookmark_Click" HorizontalAlignment="Right"/>
</DockPanel>
<Border Background="{StaticResource BorderSubtle}" Height="1" Margin="0,0,0,4"/>
<ItemsControl Name="BookmarksList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DockPanel Margin="0,1">
<Button DockPanel.Dock="Right" Style="{StaticResource CardActionButton}" Content="✕"
Click="RemoveBookmark_Click" Padding="8,5" Margin="6,0,0,0" VerticalAlignment="Center"/>
<Button Style="{StaticResource FlatButton}" Click="OpenBookmark_Click" HorizontalAlignment="Stretch">
<StackPanel>
<TextBlock Text="{Binding Title}" TextTrimming="CharacterEllipsis" FontSize="12.5" FontWeight="Medium"/>
<TextBlock Text="{Binding Url}" Foreground="{StaticResource TextMuted}" FontSize="10.5" TextTrimming="CharacterEllipsis"/>
</StackPanel>
</Button>
</DockPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
</Popup>
<!-- ===== 히스토리 플라이아웃 ===== -->
<Popup Name="HistoryPopup" PlacementTarget="{Binding ElementName=HistoryBtn}"
Placement="Bottom" HorizontalOffset="-10" VerticalOffset="6"
StaysOpen="False" AllowsTransparency="True" PopupAnimation="Fade">
<Border Background="{StaticResource CardBg}" BorderBrush="{StaticResource BorderBrush}"
BorderThickness="1" CornerRadius="12" Padding="8" Width="340" MaxHeight="480">
<StackPanel>
<DockPanel Margin="6,4,6,6">
<TextBlock Text="방문 기록" Foreground="{StaticResource TextPrimary}" FontWeight="SemiBold" VerticalAlignment="Center"/>
<Button DockPanel.Dock="Right" Style="{StaticResource CardActionButton}" Content="🗑 전체 삭제" Click="ClearHistory_Click" HorizontalAlignment="Right"/>
</DockPanel>
<Border Background="{StaticResource BorderSubtle}" Height="1" Margin="0,0,0,4"/>
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="400">
<ItemsControl Name="HistoryList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Style="{StaticResource FlatButton}" Click="OpenHistory_Click" HorizontalAlignment="Stretch" Margin="0,1">
<StackPanel>
<DockPanel>
<TextBlock DockPanel.Dock="Right" Text="{Binding VisitedAt, StringFormat=HH:mm}" Foreground="{StaticResource TextMuted}" FontSize="10.5" VerticalAlignment="Center"/>
<TextBlock Text="{Binding Title}" TextTrimming="CharacterEllipsis" FontSize="12.5" FontWeight="Medium"/>
</DockPanel>
<TextBlock Text="{Binding Url}" Foreground="{StaticResource TextMuted}" FontSize="10.5" TextTrimming="CharacterEllipsis"/>
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</StackPanel>
</Border>
</Popup>
<!-- ===== 주소창 자동완성 제안 ===== -->
<Popup Name="AddressPopup" PlacementTarget="{Binding ElementName=AddressBox}"
Placement="Bottom" StaysOpen="False" AllowsTransparency="True" PopupAnimation="Fade"
Width="560" MaxHeight="380">
<Border Background="{StaticResource CardBg}" BorderBrush="{StaticResource BorderBrush}"
BorderThickness="1" CornerRadius="10" Padding="4">
<ListBox Name="AddressSuggestions" Style="{StaticResource SuggestList}"
ItemContainerStyle="{StaticResource SuggestItem}"
PreviewMouseLeftButtonUp="AddressSuggestion_Click">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Title}" TextTrimming="CharacterEllipsis" FontSize="12.5" FontWeight="Medium"/>
<TextBlock Text="{Binding Url}" Foreground="{StaticResource TextMuted}" FontSize="10.5" TextTrimming="CharacterEllipsis"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
</Popup>
<!-- ===== 링크로 받기 플라이아웃 (YouTube/Instagram/X URL 붙여넣기) ===== -->
<Popup Name="LinkPopup" PlacementTarget="{Binding ElementName=LinkBtn}"
Placement="Bottom" HorizontalOffset="-10" VerticalOffset="6"
StaysOpen="False" AllowsTransparency="True" PopupAnimation="Fade">
<Border Background="{StaticResource CardBg}" BorderBrush="{StaticResource BorderBrush}"
BorderThickness="1" CornerRadius="12" Padding="10" Width="380">
<StackPanel>
<TextBlock Text="링크로 받기" Foreground="{StaticResource TextPrimary}" FontWeight="SemiBold" Margin="6,2,6,4"/>
<TextBlock Text="YouTube·Instagram·X 등 영상 링크를 붙여넣고 받기를 누르세요" Foreground="{StaticResource TextMuted}" FontSize="11" Margin="6,0,6,10" TextWrapping="Wrap"/>
<DockPanel>
<Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}" Content="받기" Margin="8,0,0,0" Click="LinkDownload_Click"/>
<Border Background="{StaticResource GlassBg}" BorderBrush="{StaticResource BorderBrush}" BorderThickness="1" CornerRadius="8">
<TextBox Name="LinkBox" Style="{StaticResource DarkTextBox}" BorderThickness="0" Background="Transparent"
Padding="12,8" PreviewKeyDown="LinkBox_PreviewKeyDown"/>
</Border>
</DockPanel>
</StackPanel>
</Border>
</Popup>
</Grid>
</Window>