E 묶음: 세로 막대 · 폭 되돌림 · 머리 빈 공간

E-1. 원본 세 칸에 세로 스크롤 막대를 켰다.
가로와 달리 세로는 이 칸들이 실제로 굴러가므로 ScrollViewer 가 주는 막대를
그대로 쓴다. 대신 막대가 자리를 차지하는 만큼 여백 보정을 걷어냈다. 전에는
편집기 막대 폭만큼 오른쪽 여백을 줘서 글자 폭을 맞췄는데, 이제 진짜 막대가 그
자리를 차지하므로 여백까지 주면 글자 폭이 좁아져 접히는 자리가 어긋난다.

E-2. 칸 폭을 줄였다 늘리면 행 높이가 돌아오지 않던 문제.
행 높이는 각 칸이 알려 온 글 높이 중 가장 큰 것을 쓴다. 그런데 글자를 그리는
칸이 세로로 늘어나 있어서(WPF 기본값 Stretch) 행이 한 번 커지면 그 다음부터는
"행 높이만큼 높다"고 되돌려 보냈다. 자기가 만든 값을 자기가 다시 읽는 셈이라
높이가 올라가기만 하고 내려오지 않았다. 글자 칸을 위로 붙여 자기 글 높이만
알리도록 고쳤다.

E-3. 경로 줄과 코드 사이의 빈 공간.
패널 머리 높이를 상수로 못 박아 두어(124 / 74) 실제 내용보다 훨씬 컸다.
상수를 없애고 내용에 맞춰 줄어들게 한 뒤, 나란히 놓인 칸끼리는 SharedSizeGroup
으로 가장 큰 것에 맞춘다(REMOTE 만 Conflict 줄이 하나 더 있다). 2-Way 에서는
MERGED 머리도 같은 무리에 넣고 3-Way 에서는 뺀다.
2-Way 에서 한 화면에 보이는 코드가 29줄에서 34줄로 늘었다.

확인: 세로 막대를 끌면 세 칸이 함께 움직인다. 2560x1600 <-> 1920x1080 을
오가도, 가운데 구분선을 끌었다 되돌려도 처음과 같은 화면이 된다.
테스트 399건 그대로 통과.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
김승준 2026-09-09 11:29:56 +09:00
parent 179dffb1ad
commit 9a9c431b43
4 changed files with 107 additions and 17 deletions

View File

@ -155,3 +155,24 @@
- 어떤 파일을 어떻게 고쳤는지
- 실제로 실행해 확인한 결과
- 커밋 해시
---
## E. 추가 요청 (2026-09-09 오후)
### E-1. 원본 세 칸에 세로 스크롤 막대가 없다
- B-1 에서 가로 막대를 붙였는데 세로가 빠져 있었다.
- **판정 기준**: 세 칸 오른쪽에 막대가 보이고, 하나를 끌면 셋이 함께 움직인다.
### E-2. 칸 폭을 줄였다 늘리면 행 높이가 돌아오지 않는다
- 증상: 화면을 좁혔다가 다시 넓히면 접혔던 행이 <b>넓은 자리를 그대로 차지한 채</b> 남는다.
- 창을 옮겨 크기가 바뀌는 경우와 가운데 구분선을 끄는 경우 둘 다 확인한다.
- **판정 기준**: 좁혔다 되돌리면 처음과 똑같은 화면이 된다.
### E-3. 경로 줄과 코드 사이의 빈 공간을 없앤다
- 증상: 패널 머리(제목·경로) 아래에 쓰이지 않는 빈 자리가 넓게 남아 코드가 그만큼 덜 보인다.
- 나란히 놓인 칸끼리는 머리 높이가 같아야 첫 줄이 같은 눈높이에 온다. 그 조건은 지킨다.
- **판정 기준**: 경로 줄 바로 아래에서 코드가 시작하고, 두(세) 칸의 01행이 같은 높이에 있다.

View File

@ -134,6 +134,53 @@ CommunityToolkit 커맨드는 CommandManager 를 구독하지 않는데 이 둘
| 대기 | D-6 비교 결과 텍스트 내보내기 |
| 대기 | D-7 드래그 앤 드롭으로 열기 |
### E. 추가 요청 (2026-09-09 오후)
| 상태 | 항목 |
|---|---|
| **완료** | E-1 원본 세 칸에 세로 스크롤 막대 |
| **완료** | E-2 칸 폭을 줄였다 늘리면 행 높이가 안 돌아옴 |
| **완료** | E-3 경로 줄과 코드 사이의 빈 공간 |
---
### E. 추가 요청 세 가지 — **완료** (2026-09-09)
#### E-1. 원본 세 칸에 세로 스크롤 막대
가로(B-1)만 붙이고 세로가 빠져 있었다. 세 칸에 `VerticalScrollBarVisibility="Visible"` 을 켰다.
가로와 달리 세로는 이 칸들이 <b>실제로 굴러가므로</b> 따로 막대를 만들 필요 없이 ScrollViewer 가
주는 것을 그대로 쓴다. 대신 막대가 자리를 차지하는 만큼 <b>여백 보정을 걷어냈다</b> -
전에는 편집기 막대 폭만큼 오른쪽 여백을 줘서 글자 폭을 맞췄는데, 이제 진짜 막대가 그 자리를
차지하므로 여백까지 주면 글자 폭이 좁아져 접히는 자리가 어긋난다.
#### E-2. 칸 폭을 줄였다 늘리면 행 높이가 돌아오지 않던 문제
화면을 좁혔다 되돌리면 접혔던 행이 <b>넓은 자리를 그대로 차지한 채</b> 남았다.
행 높이는 각 칸이 "내 글이 이만큼 높다"고 알려 온 값 중 <b>가장 큰 것</b>을 쓴다.
워드랩에서 한쪽만 접힌 행이 있으면 그쪽에 맞춰야 옆 칸과 줄이 맞물리기 때문이다.
그런데 글자를 그리는 칸이 세로로 <b>늘어나 있어서</b>(WPF 기본값 Stretch) 행이 한 번 커지면
그 다음부터는 "행 높이만큼 높다"고 되돌려 보냈다. 자기가 만든 값을 자기가 다시 읽는 셈이라
<b>높이가 올라가기만 하고 내려오지 않았다.</b>
글자 칸을 위로 붙여(`VerticalAlignment="Top"`) 자기 글 높이만 알리도록 고쳤다.
#### E-3. 경로 줄과 코드 사이의 빈 공간
패널 머리 높이를 상수로 못 박아 두었다(124 / 74). 실제 내용보다 훨씬 커서 경로 줄 아래가
휑하게 비었고 그만큼 코드가 덜 보였다. 상수를 없애고 내용에 맞춰 줄어들게 한 뒤,
나란히 놓인 칸끼리는 `SharedSizeGroup` 으로 <b>가장 큰 것에 맞추게</b> 했다
(REMOTE 만 "Conflict N" 줄이 하나 더 있어서 그렇게 해야 첫 줄이 같은 눈높이에 온다).
2-Way 에서는 MERGED 머리도 같은 무리에 넣고, 3-Way 에서는 뺀다 - 3-Way 의 MERGED 는
아래 줄로 내려가 맞출 상대가 없는데, 넣어 두면 그쪽 경로 줄 때문에 위 세 칸까지 덩달아 커진다.
**확인**: 2-Way 에서 한 화면에 보이는 코드가 <b>29줄에서 34줄로</b> 늘었다.
BASE·LOCAL 의 01행은 같은 높이 그대로다.
**세 가지 모두 확인**: 세로 막대를 끌면 세 칸이 함께 움직인다.
2560×1600 ↔ 1920×1080 을 오가도, 가운데 구분선을 끌었다 되돌려도 처음과 같은 화면이 된다.
테스트 399건 그대로 통과.
---
### B. 3-Way 화면 — **완료** (2026-09-09)

View File

@ -125,6 +125,7 @@
FontFamily="Consolas" FontSize="12"
LineHeight="{StaticResource CodeLineHeight}"
LineStackingStrategy="BlockLineHeight"
VerticalAlignment="Top"
SizeChanged="BaseText_SizeChanged"
ToolTip="{Binding BaseText}">
<ctl:CodeTextBlock.RenderTransform>
@ -175,6 +176,7 @@
FontFamily="Consolas" FontSize="12"
LineHeight="{StaticResource CodeLineHeight}"
LineStackingStrategy="BlockLineHeight"
VerticalAlignment="Top"
SizeChanged="OriginalLocalText_SizeChanged"
ToolTip="{Binding OriginalLocalText}">
<ctl:CodeTextBlock.RenderTransform>
@ -225,6 +227,7 @@
FontFamily="Consolas" FontSize="12"
LineHeight="{StaticResource CodeLineHeight}"
LineStackingStrategy="BlockLineHeight"
VerticalAlignment="Top"
SizeChanged="RemoteText_SizeChanged"
ToolTip="{Binding RemoteText}">
<ctl:CodeTextBlock.RenderTransform>
@ -317,6 +320,7 @@
FontFamily="Consolas" FontSize="12"
LineHeight="{StaticResource CodeLineHeight}"
LineStackingStrategy="BlockLineHeight"
VerticalAlignment="Top"
SizeChanged="LocalText_SizeChanged">
<ctl:CodeTextBlock.RenderTransform>
<TranslateTransform X="{Binding TextShiftX,
@ -622,7 +626,7 @@
<!-- ===== 본문 4분할 =====
ScrollViewer 로 감싸면 내부 Grid 가 "무한 폭"으로 측정되어 star 열이 콘텐츠 크기로
부풀고 오른쪽 패널이 창 밖으로 밀려난다. 셸이 이미 정확한 폭을 주므로 그대로 채운다. -->
<Grid Grid.Row="2" x:Name="BodyGrid">
<Grid Grid.Row="2" x:Name="BodyGrid" Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<!-- Git 저장소가 아니면 커밋 이력 칸을 접는다. 보여 줄 이력이 없기 때문이다. -->
<ColumnDefinition MinWidth="0">
@ -809,12 +813,12 @@
BorderBrush="{DynamicResource Ubisam.Brush.Input.Border}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" SharedSizeGroup="PaneHeader"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="8,6" Height="{Binding SourceHeaderHeight}">
<StackPanel Grid.Row="0" Margin="8,6">
<TextBlock Text="LOCAL (Mine)" Style="{StaticResource PanelTitle}"/>
<TextBlock Text="{Binding LocalHeader}" Style="{StaticResource Muted}" FontSize="11"
TextTrimming="CharacterEllipsis"/>
@ -826,7 +830,7 @@
ItemContainerStyle="{StaticResource CodeLineItem}"
VirtualizingPanel.ScrollUnit="Pixel"
HorizontalContentAlignment="Stretch"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.HorizontalScrollBarVisibility="{Binding SourceBars,
RelativeSource={RelativeSource AncestorType=UserControl}}"
ScrollViewer.ScrollChanged="Lines_ScrollChanged"/>
@ -877,13 +881,13 @@
</Border.Style>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" SharedSizeGroup="PaneHeader"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- BASE / LOCAL 의 머리 영역 높이를 같게 고정해야 두 패널의 01행이 같은 눈높이에 온다 -->
<StackPanel Grid.Row="0" Margin="8,6" Height="{Binding SourceHeaderHeight}">
<StackPanel Grid.Row="0" Margin="8,6">
<TextBlock Text="{Binding BaseTitle}" Style="{StaticResource PanelTitle}"/>
<TextBlock Text="{Binding BaseHeader}" Style="{StaticResource Muted}" FontSize="11"
TextTrimming="CharacterEllipsis"/>
@ -914,7 +918,7 @@
ItemContainerStyle="{StaticResource CodeLineItem}"
VirtualizingPanel.ScrollUnit="Pixel"
HorizontalContentAlignment="Stretch"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.HorizontalScrollBarVisibility="{Binding SourceBars,
RelativeSource={RelativeSource AncestorType=UserControl}}"
ScrollViewer.ScrollChanged="Lines_ScrollChanged"/>
@ -973,7 +977,7 @@
</Border.Style>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition x:Name="MergedHeaderRow" Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
@ -986,7 +990,7 @@
BASE 의 경로와 높이가 어긋난다. 어차피 줄 단위 반영·되돌리기 버튼이
ACTIONS 에 있으니 같은 성격끼리 한자리에 모이는 편이 낫기도 하다.
-->
<StackPanel Grid.Row="0" Margin="8,6" Height="{StaticResource PaneHeaderHeight}">
<StackPanel Grid.Row="0" Margin="8,6">
<TextBlock Text="{Binding LocalTitle}" Style="{StaticResource PanelTitle}"/>
<TextBlock Text="{Binding LocalHeader}" Style="{StaticResource Muted}" FontSize="11"
TextTrimming="CharacterEllipsis"/>
@ -1232,7 +1236,7 @@
BorderBrush="{DynamicResource Ubisam.Brush.Input.Border}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" SharedSizeGroup="PaneHeader"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
@ -1240,7 +1244,7 @@
<!-- BASE/LOCAL 과 같은 머리 높이. 세 패널의 01행이 같은 눈높이에 와야 한다.
RowDefinition 이 아니라 StackPanel 에 거는 이유 - 리소스가 Double 이라
GridLength 자리에 넣으면 화면을 만들 때 그대로 터진다. -->
<StackPanel Grid.Row="0" Margin="8,6" Height="{Binding SourceHeaderHeight}">
<StackPanel Grid.Row="0" Margin="8,6">
<TextBlock Text="REMOTE (Theirs)" Style="{StaticResource PanelTitle}"/>
<TextBlock Text="{Binding RemoteHeader}" Style="{StaticResource Muted}"
FontSize="11" TextTrimming="CharacterEllipsis"/>
@ -1261,7 +1265,7 @@
ItemContainerStyle="{StaticResource CodeLineItem}"
VirtualizingPanel.ScrollUnit="Pixel"
HorizontalContentAlignment="Stretch"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.HorizontalScrollBarVisibility="{Binding SourceBars,
RelativeSource={RelativeSource AncestorType=UserControl}}"
ScrollViewer.ScrollChanged="Lines_ScrollChanged"/>

View File

@ -453,13 +453,31 @@ namespace MergeHub.Views
var bar = SystemParameters.VerticalScrollBarWidth;
var threeWay = (DataContext as HomeViewModel)?.IsThreeWay == true;
LocalLines.Margin = new Thickness(0, 0, bar, 0);
RemoteLines.Margin = new Thickness(0, 0, bar, 0);
OriginalLocalLines.Margin = new Thickness(0, 0, bar, 0);
// 패널 머리(제목·경로)의 높이를 서로 맞춘다.
//
// 예전에는 상수로 못 박아 두었는데(124 / 74), 실제 내용보다 훨씬 커서
// <b>경로 줄과 코드 사이가 휑하게 비었다.</b> 이제 내용에 맞춰 줄어들고,
// 서로 다른 패널끼리는 SharedSizeGroup 이 가장 큰 것에 맞춰 준다
// (REMOTE 만 "Conflict N" 줄이 하나 더 있다).
//
// 2-Way 에서는 BASE 와 LOCAL(편집기 칸)이 나란히 있으므로 MERGED 머리도 같은 무리에 넣는다.
// 3-Way 에서는 MERGED 가 아래 줄로 내려가 맞출 상대가 없다 - 넣어 두면 그쪽 경로 줄 때문에
// 위 세 칸의 머리까지 덩달아 커진다.
MergedHeaderRow.SharedSizeGroup = threeWay ? null : "PaneHeader";
// 편집기 아래층은 편집기의 세로 막대만큼 물러난다 - 그 막대는 편집기가 그린다.
LocalLines.Margin = new Thickness(0, 0, bar, 0);
// 원본 세 칸은 <b>자기 세로 막대를 직접 그린다.</b> 그 막대가 이미 자리를 차지하므로
// 여백으로 또 물러나면 그만큼 글자 폭이 좁아져 접히는 자리가 어긋난다.
RemoteLines.Margin = new Thickness(0);
OriginalLocalLines.Margin = new Thickness(0);
// 2-Way 의 BASE 는 LOCAL 과 폭을 맞춰야 한다. LOCAL 쪽 칸에는 미니맵이 붙어 있으므로
// 그 폭만큼만 더 물러난다(막대 몫은 자기 막대가 이미 가져갔다).
BaseLines.Margin = threeWay
? new Thickness(0, 0, bar, 0)
: new Thickness(0, 0, bar + MiniMap.Width, 0);
? new Thickness(0)
: new Thickness(0, 0, MiniMap.Width, 0);
// 칸 폭이 달라졌으니 원본 칸 막대의 범위도 다시 잰다.
ApplySourceScrollUnit();