D-7 끌어다 놓기를 화면에서 확인하고 세 군데를 고쳤다

경로 칸(TextBox)이 Drop 을 먼저 먹어 놓아도 아무 일이 없었다 - Preview 로 옮겼다.
파일 비교의 경로 글자는 Background 가 없어 놓을 자리 자체가 없었다.
칸에 놓아도 다시 견주지 않던 것을 DropFile / DropFolder 로 고쳤다.
파일을 하나씩 놓아 짝을 만들 수 있게 했다.
검색 결과 칸에 머리글을 넣고 줄 간격을 목록과 맞췄다.
This commit is contained in:
김승준 2026-09-09 15:32:52 +09:00
parent 373990b3b6
commit 196af0a913
7 changed files with 155 additions and 47 deletions

View File

@ -132,7 +132,7 @@ CommunityToolkit 커맨드는 CommandManager 를 구독하지 않는데 이 둘
| ~~취소~~ | ~~D-4 비교 창 여러 개~~ (2026-09-09 사용자 판단으로 뺌) | | ~~취소~~ | ~~D-4 비교 창 여러 개~~ (2026-09-09 사용자 판단으로 뺌) |
| **완료** | D-5 덩어리 단위 반영 (3-Way 에서 BASE 를 가져오던 것을 REMOTE 로) | | **완료** | D-5 덩어리 단위 반영 (3-Way 에서 BASE 를 가져오던 것을 REMOTE 로) |
| **완료** | D-6 비교 결과 텍스트 내보내기 | | **완료** | D-6 비교 결과 텍스트 내보내기 |
| **완료**(화면 확인 남음) | D-7 드래그 앤 드롭으로 열기 | | **완료** | D-7 드래그 앤 드롭으로 열기 |
### E. 추가 요청 (2026-09-09 오후) ### E. 추가 요청 (2026-09-09 오후)
@ -203,7 +203,7 @@ BASE 머리글에 그렇게 적었다 - `BASE (merge base) · 이 파일 없음
--- ---
### D-7. 드래그 앤 드롭으로 열기 — **코드 완료 · 화면 확인 남음** (2026-09-09) ### D-7. 드래그 앤 드롭으로 열기 — **완료** (2026-09-09)
탐색기에서 파일·폴더를 끌어다 놓으면 그 자리에서 비교가 열린다. 탐색기에서 파일·폴더를 끌어다 놓으면 그 자리에서 비교가 열린다.
@ -212,8 +212,31 @@ BASE 머리글에 그렇게 적었다 - `BASE (merge base) · 이 파일 없음
가고 해당 탭이 켜진다. 화면 아무 데나 놓으면 이 판단을 타고, 왼쪽·오른쪽 경로 칸에 바로 놓으면 가고 해당 탭이 켜진다. 화면 아무 데나 놓으면 이 판단을 타고, 왼쪽·오른쪽 경로 칸에 바로 놓으면
그 칸만 채운다. 그 칸만 채운다.
<b>화면에서 실제로 끌어다 놓는 확인은 아직 못 했다.</b> 작업 중 컴퓨터가 잠겨 탐색기에서 **실제로 탐색기에서 끌어다 놓아 보니 세 가지가 걸렸다.** 코드만 보고는 알 수 없는 것들이다.
끄는 동작을 만들 수 없었다. 잠금이 풀리면 확인한다.
<b>① 경로 칸 위에 놓으면 아무 일도 일어나지 않았다.</b>
폴더 비교의 경로 칸은 `TextBox` 인데, `TextBoxBase` 의 클래스 처리기가 `DragOver`·`Drop` 을
<b>먼저</b> 먹는다. 그쪽은 글자 끌어다 놓기만 알아서 파일 목록은 버리고 `Handled` 로 막아 버린다.
터널링 쪽(`PreviewDragOver`·`PreviewDrop`)으로 옮겨 클래스 처리기보다 먼저 받게 고쳤다.
<b>② 파일 비교 화면의 경로 글자는 놓을 자리 자체가 없었다.</b>
`TextBlock` 은 `Background` 가 없으면 히트 테스트에 안 잡힌다. `Background="Transparent"` 를 줬다.
<b>③ 칸에 놓아도 다시 견주지 않았다.</b> 칸 값만 바뀌고 화면은 옛 파일을 그대로 보여 줬다 -
"놓았는데 아무 일도 없다"로 읽힌다. `HomeViewModel.DropFile` 과 `FolderViewModel.DropFolder` 를
만들어 넣은 뒤 바로 다시 견주게 했다. 폴더는 양쪽이 다 찼을 때만 견준다.
**파일 하나씩 끌어다 놓아 짝을 만들 수 있게 했다.** 그 전에는 두 번째 파일이 첫 번째를 밀어내
아무리 놓아도 왼쪽만 바뀌었다. 왼쪽만 차 있고 오른쪽이 비어 있으면 오른쪽을 채운다.
**확인한 것**
| 놓은 것 | 결과 |
|---|---|
| 폴더를 왼쪽·오른쪽 칸에 하나씩 | 두 칸이 차고 곧바로 비교가 돌았다 (`전체 4개 · 다름 1 · 왼쪽만 1 · 오른쪽만 1 · 같음 1`) |
| 폴더를 화면 몸통에 | 왼쪽 칸에 들어갔다 |
| 파일을 폴더 비교 몸통에 | 파일 비교 탭으로 넘어가고 BASE 에 들어갔다 |
| 그 뒤 파일을 하나 더 | LOCAL 에 들어가고 짝이 맞춰졌다 (`총 8 라인 · 차이 2 라인`) |
--- ---

View File

@ -343,6 +343,23 @@ namespace MergeHub.ViewModels
private CancellationTokenSource? _compareCancel; private CancellationTokenSource? _compareCancel;
/// <summary>
/// 끌어다 놓은 폴더를 한쪽 칸에 넣고, <b>양쪽이 다 차면 바로 견준다.</b>
///
/// 탐색기 우클릭으로 넘어오는 길과 같게 맞춘 것이다 - 두 칸이 다 찼는데도
/// [비교]를 한 번 더 눌러야 하면 놓은 보람이 없다.
/// </summary>
public void DropFolder(string folder, bool left)
{
if (string.IsNullOrWhiteSpace(folder)) return;
if (left) LeftFolder = folder;
else RightFolder = folder;
if (Directory.Exists(LeftFolder) && Directory.Exists(RightFolder)) _ = CompareAsync();
}
// ================= 폴더 전체 검색 ================= // ================= 폴더 전체 검색 =================
/// <summary>찾은 줄. 파일 차례로 늘어선다.</summary> /// <summary>찾은 줄. 파일 차례로 늘어선다.</summary>

View File

@ -1444,6 +1444,24 @@ namespace MergeHub.ViewModels
} }
} }
/// <summary>
/// 끌어다 놓은 파일을 한쪽 칸에 넣고 <b>바로 다시 견준다.</b>
///
/// 칸 값만 바꾸고 두면 화면은 옛 파일을 그대로 보여 준다 - "놓았는데 아무 일도 없다"로 읽힌다.
/// 파일 고르기 단추와 같은 길(<c>RequestPlainCompare</c>)로 보내는 까닭은 그 길이
/// Git 모드를 끄는 것까지 함께 하기 때문이다. 저장소를 열어 둔 채로 파일을 끌어다 놓으면
/// 사용자가 보고 싶은 것은 그 두 파일이지 커밋 이력이 아니다.
/// </summary>
public void DropFile(string path, bool left)
{
if (string.IsNullOrWhiteSpace(path)) return;
var newLeft = left ? path : LeftFile;
var newRight = left ? RightFile : path;
_workspace.RequestPlainCompare(newLeft, newRight);
}
// ================= 결과 내보내기 ================= // ================= 결과 내보내기 =================
private bool CanExportReport() => Lines.Count > 0; private bool CanExportReport() => Lines.Count > 0;

View File

@ -102,14 +102,14 @@
<Button Grid.Column="0" Content="왼쪽 폴더..." Command="{Binding PickLeftFolderCommand}" <Button Grid.Column="0" Content="왼쪽 폴더..." Command="{Binding PickLeftFolderCommand}"
Height="28" Padding="10,2" FontSize="11" Margin="0,0,6,0"/> Height="28" Padding="10,2" FontSize="11" Margin="0,0,6,0"/>
<TextBox Grid.Column="1" Text="{Binding LeftFolder, UpdateSourceTrigger=PropertyChanged}" <TextBox Grid.Column="1" Text="{Binding LeftFolder, UpdateSourceTrigger=PropertyChanged}"
AllowDrop="True" DragOver="View_DragOver" Drop="LeftFolder_Drop" AllowDrop="True" PreviewDragOver="View_DragOver" PreviewDrop="LeftFolder_Drop"
FontFamily="Consolas" FontSize="11" Height="28" VerticalContentAlignment="Center" FontFamily="Consolas" FontSize="11" Height="28" VerticalContentAlignment="Center"
Margin="0,0,14,0" ToolTip="비교 기준이 되는 폴더"/> Margin="0,0,14,0" ToolTip="비교 기준이 되는 폴더"/>
<Button Grid.Column="2" Content="오른쪽 폴더..." Command="{Binding PickRightFolderCommand}" <Button Grid.Column="2" Content="오른쪽 폴더..." Command="{Binding PickRightFolderCommand}"
Height="28" Padding="10,2" FontSize="11" Margin="0,0,6,0"/> Height="28" Padding="10,2" FontSize="11" Margin="0,0,6,0"/>
<TextBox Grid.Column="3" Text="{Binding RightFolder, UpdateSourceTrigger=PropertyChanged}" <TextBox Grid.Column="3" Text="{Binding RightFolder, UpdateSourceTrigger=PropertyChanged}"
AllowDrop="True" DragOver="View_DragOver" Drop="RightFolder_Drop" AllowDrop="True" PreviewDragOver="View_DragOver" PreviewDrop="RightFolder_Drop"
FontFamily="Consolas" FontSize="11" Height="28" VerticalContentAlignment="Center" FontFamily="Consolas" FontSize="11" Height="28" VerticalContentAlignment="Center"
ToolTip="견줄 폴더"/> ToolTip="견줄 폴더"/>
</Grid> </Grid>
@ -302,40 +302,70 @@
찾은 줄. 검색하기 전에는 접혀 있어 목록이 자리를 다 쓴다. 찾은 줄. 검색하기 전에는 접혀 있어 목록이 자리를 다 쓴다.
높이를 묶어 두는 까닭 - 결과가 수천 줄이어도 목록을 화면 밖으로 밀면 안 된다. 높이를 묶어 두는 까닭 - 결과가 수천 줄이어도 목록을 화면 밖으로 밀면 안 된다.
--> -->
<Border Grid.Row="1" BorderThickness="1,0,1,1" Margin="0,0,0,0" <Grid Grid.Row="1"
BorderBrush="{DynamicResource Ubisam.Brush.Input.Border}" Visibility="{Binding HasSearched, Converter={StaticResource BoolToVis}}">
Visibility="{Binding HasSearched, Converter={StaticResource BoolToVis}}"> <Grid.RowDefinitions>
<ListBox x:Name="HitList" ItemsSource="{Binding SearchHits}" <RowDefinition Height="Auto"/>
SelectedItem="{Binding SelectedHit}" <RowDefinition Height="Auto"/>
MinHeight="90" MaxHeight="260" BorderThickness="0" </Grid.RowDefinitions>
MouseDoubleClick="HitList_MouseDoubleClick"
KeyDown="HitList_KeyDown"
HorizontalContentAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="52"/>
<ColumnDefinition Width="320"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding SideText}" FontSize="11" <!-- 결과 칸 머리글. 위 목록과 같은 짜임이라 여기도 무슨 칸인지 적어 준다. -->
FontWeight="Bold"/> <Border Grid.Row="0" Padding="8,4" BorderThickness="1,1,1,0"
<TextBlock Grid.Column="1" Text="{Binding Where}" FontFamily="Consolas" Background="{DynamicResource Ubisam.Brush.Sidebar.Background}"
FontSize="11" TextTrimming="CharacterEllipsis" BorderBrush="{DynamicResource Ubisam.Brush.Input.Border}">
ToolTip="{Binding RelativePath}"/> <Grid Margin="6,0,0,0">
<TextBlock Grid.Column="2" Text="{Binding Preview}" FontFamily="Consolas" <Grid.ColumnDefinitions>
FontSize="11" TextTrimming="CharacterEllipsis" <ColumnDefinition Width="52"/>
Style="{StaticResource Muted}"/> <ColumnDefinition Width="280"/>
</Grid> <ColumnDefinition Width="*"/>
</DataTemplate> </Grid.ColumnDefinitions>
</ListBox.ItemTemplate> <TextBlock Grid.Column="0" Text="어디" FontSize="11" FontWeight="Bold"
</ListBox> ToolTip="양쪽 = 두 폴더에 똑같이 있는 줄"/>
</Border> <TextBlock Grid.Column="1" Text="파일 · 줄" FontSize="11" FontWeight="Bold"/>
<TextBlock Grid.Column="2" Text="찾은 줄" FontSize="11" FontWeight="Bold"/>
</Grid>
</Border>
<Border Grid.Row="1" BorderThickness="1,0,1,1"
BorderBrush="{DynamicResource Ubisam.Brush.Input.Border}">
<ListBox x:Name="HitList" ItemsSource="{Binding SearchHits}"
SelectedItem="{Binding SelectedHit}"
MinHeight="90" MaxHeight="260" BorderThickness="0"
MouseDoubleClick="HitList_MouseDoubleClick"
KeyDown="HitList_KeyDown"
HorizontalContentAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling">
<!-- 기본 여백을 그대로 두면 줄이 성기게 벌어져 위 목록과 딴판으로 보인다. -->
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Padding" Value="6,1"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="52"/>
<ColumnDefinition Width="280"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding SideText}" FontSize="11"
FontWeight="Bold"/>
<TextBlock Grid.Column="1" Text="{Binding Where}" FontFamily="Consolas"
FontSize="11" TextTrimming="CharacterEllipsis"
ToolTip="{Binding RelativePath}"/>
<TextBlock Grid.Column="2" Text="{Binding Preview}" FontFamily="Consolas"
FontSize="11" TextTrimming="CharacterEllipsis"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
</Grid>
</Grid> </Grid>
<!-- ⑤ 요약 · 동작 --> <!-- ⑤ 요약 · 동작 -->

View File

@ -139,7 +139,15 @@ namespace MergeHub.Views
e.Handled = true; e.Handled = true;
} }
/// <summary>왼쪽·오른쪽 칸에 바로 놓으면 <b>그 자리에만</b> 들어간다.</summary> /// <summary>
/// 왼쪽·오른쪽 칸에 바로 놓으면 <b>그 자리에만</b> 들어간다.
///
/// XAML 에서 <c>PreviewDrop</c> 으로 받는 까닭 - 경로 칸이 <c>TextBox</c> 라서
/// <c>TextBoxBase</c> 의 클래스 처리기가 <c>DragOver</c>·<c>Drop</c> 을 먼저 먹는다.
/// 그쪽은 글자 끌어다 놓기만 알아서 파일 목록은 버리고 <c>Handled</c> 로 막아 버린다.
/// 실제로 화면에서 칸 위에 놓아 봤더니 아무 일도 일어나지 않았다.
/// 터널링(Preview)은 클래스 처리기보다 먼저 도므로 이쪽이 먼저 받는다.
/// </summary>
private void LeftFolder_Drop(object sender, DragEventArgs e) => DropIntoSlot(e, left: true); private void LeftFolder_Drop(object sender, DragEventArgs e) => DropIntoSlot(e, left: true);
private void RightFolder_Drop(object sender, DragEventArgs e) => DropIntoSlot(e, left: false); private void RightFolder_Drop(object sender, DragEventArgs e) => DropIntoSlot(e, left: false);
@ -154,9 +162,6 @@ namespace MergeHub.Views
if (folder.Length == 0) folder = Services.PathUtil.FolderOfFile(paths[0]); if (folder.Length == 0) folder = Services.PathUtil.FolderOfFile(paths[0]);
if (folder.Length == 0) return; if (folder.Length == 0) return;
if (left) vm.LeftFolder = folder;
else vm.RightFolder = folder;
// 두 개를 한꺼번에 놓았으면 나머지 하나는 반대쪽에 넣는다. // 두 개를 한꺼번에 놓았으면 나머지 하나는 반대쪽에 넣는다.
if (paths.Length > 1) if (paths.Length > 1)
{ {
@ -170,6 +175,9 @@ namespace MergeHub.Views
} }
} }
// 넣고 나서 양쪽이 다 찼으면 그 자리에서 견준다.
vm.DropFolder(folder, left);
e.Handled = true; e.Handled = true;
} }

View File

@ -386,6 +386,7 @@
<TextBlock Text="{Binding LeftFile}" Style="{StaticResource Muted}" FontSize="11" <TextBlock Text="{Binding LeftFile}" Style="{StaticResource Muted}" FontSize="11"
MaxWidth="260" TextTrimming="CharacterEllipsis" Margin="4,0,10,0" MaxWidth="260" TextTrimming="CharacterEllipsis" Margin="4,0,10,0"
ToolTip="{Binding LeftFile}" ToolTip="{Binding LeftFile}"
Background="Transparent"
AllowDrop="True" DragOver="View_DragOver" Drop="LeftFile_Drop"/> AllowDrop="True" DragOver="View_DragOver" Drop="LeftFile_Drop"/>
<Button Content="내 파일..." Command="{Binding PickRightFileCommand}" <Button Content="내 파일..." Command="{Binding PickRightFileCommand}"
Height="26" MinHeight="26" Padding="8,2" FontSize="11" Height="26" MinHeight="26" Padding="8,2" FontSize="11"
@ -393,6 +394,7 @@
<TextBlock Text="{Binding RightFile}" Style="{StaticResource Muted}" FontSize="11" <TextBlock Text="{Binding RightFile}" Style="{StaticResource Muted}" FontSize="11"
MaxWidth="260" TextTrimming="CharacterEllipsis" Margin="4,0,0,0" MaxWidth="260" TextTrimming="CharacterEllipsis" Margin="4,0,0,0"
ToolTip="{Binding RightFile}" ToolTip="{Binding RightFile}"
Background="Transparent"
AllowDrop="True" DragOver="View_DragOver" Drop="RightFile_Drop"/> AllowDrop="True" DragOver="View_DragOver" Drop="RightFile_Drop"/>
</StackPanel> </StackPanel>

View File

@ -936,6 +936,16 @@ namespace MergeHub.Views
if (DataContext is not HomeViewModel vm) return; if (DataContext is not HomeViewModel vm) return;
if (DroppedPaths(e) is not { Length: > 0 } paths) return; if (DroppedPaths(e) is not { Length: > 0 } paths) return;
// 파일 하나를 놓았는데 왼쪽만 차 있으면 오른쪽을 채운다.
// 그러지 않으면 하나씩 끌어다 놓아서는 짝을 만들 수 없다 - 두 번째가 첫 번째를 밀어낸다.
if (paths.Length == 1 && System.IO.File.Exists(paths[0]) &&
!string.IsNullOrWhiteSpace(vm.LeftFile) && string.IsNullOrWhiteSpace(vm.RightFile))
{
vm.DropFile(paths[0], left: false);
e.Handled = true;
return;
}
vm.Workspace.RouteDroppedPaths(paths); vm.Workspace.RouteDroppedPaths(paths);
e.Handled = true; e.Handled = true;
} }
@ -958,9 +968,6 @@ namespace MergeHub.Views
return; return;
} }
if (left) vm.LeftFile = paths[0];
else vm.RightFile = paths[0];
// 두 개를 한꺼번에 놓았으면 나머지 하나는 반대쪽에 넣는다. // 두 개를 한꺼번에 놓았으면 나머지 하나는 반대쪽에 넣는다.
if (paths.Length > 1 && !System.IO.Directory.Exists(paths[1])) if (paths.Length > 1 && !System.IO.Directory.Exists(paths[1]))
{ {
@ -968,6 +975,9 @@ namespace MergeHub.Views
else vm.LeftFile = paths[1]; else vm.LeftFile = paths[1];
} }
// 칸 값만 바꾸면 화면은 옛 파일을 그대로 보여 준다. 넣고 바로 다시 견준다.
vm.DropFile(paths[0], left);
e.Handled = true; e.Handled = true;
} }