11차 F-5 뒤: 켜자마자 · 탭에서 돌아와도 단축키가 듣게(편집기 자동 포커스)

- 포커스가 비었거나 이 화면 밖이면 편집기로(화면 뜰 때 · 돌아올 때 · 창 활성)
- 화면 안(찾기 칸 등) · 다른 창의 포커스는 그대로
- E-1 병합 길 회귀 없음

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
김승준 2026-09-22 18:16:39 +09:00
parent 77621b1023
commit 1895b11c8d
4 changed files with 52 additions and 2 deletions

View File

@ -729,6 +729,14 @@ libgit2 의 다듬기(`PrettifyMessages`)를 켜면 **손대지 않은 뒤 커
- 편집기에 포커스가 있으면 **Ctrl+=** 13 → 14(행 34px) · **Ctrl+-** → 13 · **Ctrl+0** → 기본(12, 행 30px), 매번 저장. - 편집기에 포커스가 있으면 **Ctrl+=** 13 → 14(행 34px) · **Ctrl+-** → 13 · **Ctrl+0** → 기본(12, 행 30px), 매번 저장.
- **켜자마자는 포커스가 아무 데도 없어**(`FocusedElement` 빈 값) Ctrl+= 뿐 아니라 F9 같은 **다른 단축키도 듣지 않는다** — F-5 가 아니라 원래 있던 동작(코드 칸을 한 번 누르면 된다). 고칠지는 사용자에게 물음. - **켜자마자는 포커스가 아무 데도 없어**(`FocusedElement` 빈 값) Ctrl+= 뿐 아니라 F9 같은 **다른 단축키도 듣지 않는다** — F-5 가 아니라 원래 있던 동작(코드 칸을 한 번 누르면 된다). 고칠지는 사용자에게 물음.
### 이어서 고친 것 — 켜자마자 단추키가 안 듣던 것 (2026-09-22 사용자 "응 고쳐줘")
- `HomeView.FocusEditorWhenIdle` — 화면이 처음 뜰 때 · 파일 비교 탭으로 돌아올 때 · 창이 활성이 될 때, 키보드 포커스가 **비어 있거나 이 화면 밖**(아래 탭 줄 등)이면 편집기로 준다. 이 화면 안(찾기 칸 · 가는 줄 칸 등)이나 **다른 창**(대화상자)에 있는 포커스는 건드리지 않는다. 켤 때는 사용자 이름 창이 먼저 떠 있으므로 논리 포커스만 정해 두고, 본 창이 활성이 되면 WPF 가 거기로 돌려준다.
- 확인(`f5-focus.ps1`): 켠 직후 아무 데도 안 누르고 포커스 `LocalEditor` · F9 들음, Git 탭(포커스는 탭 줄 항목)에 갔다 돌아오면 `LocalEditor` · F9 들음, Ctrl+F 뒤 `FindBox` 가 2초 뒤에도 그대로. 빌드 경고 0 · 오류 0.
- 회귀: 11차 E-1 병합 길(마우스 확인 B-1 · 스크린숏만 끔) — 1) 24 → 9 · 누른 Take 18 = 로그 18 · 병합 커밋 본문 26개 · 줄끝 · BOM 바뀐 파일 0 · 충돌 표식 0 · class 검사 0.
- 첫 회차는 "다음 충돌 안 보임" 으로 멈췄다 — 사용자가 그 사이 직접 MergeHub 를 켜 글자 9 · ACTIONS 감춤으로 바꿔 두었고, 시험 도구는 설정을 그대로 썼다. 시험 도구(`d7lib.ps1` `WithSettings`)가 **시험하는 동안만** ACTIONS 보임 · 글자 12 로 두고 끝나면 바이트 그대로 되돌리게 고쳤다(제품 아님).
- 같은 때 시험 스크립트가 사용자가 켜 둔 MergeHub 를 강제로 껐다(16:55). 앞으로 시험 전에 떠 있는 창을 보고, 있으면 먼저 묻는다.
## F-12. C# 코드 스타일 맞춤의 기본값을 AURORA 로 (2026-09-18, 완료) ## F-12. C# 코드 스타일 맞춤의 기본값을 AURORA 로 (2026-09-18, 완료)
사용자: "기본값은 원본형태를 따르고 C#인 경우에 AURORA 스타일로 하자" → 물어서 정한 것은 **제품 기본값 AURORA · C# 파일만 · 저장 동작은 지금 그대로**(켜져 있으면 저장도 그 스타일로 쓴다). 사용자: "기본값은 원본형태를 따르고 C#인 경우에 AURORA 스타일로 하자" → 물어서 정한 것은 **제품 기본값 AURORA · C# 파일만 · 저장 동작은 지금 그대로**(켜져 있으면 저장도 그 스타일로 쓴다).

View File

@ -8,6 +8,7 @@
| 날짜 | 항목 | 커밋 | | 날짜 | 항목 | 커밋 |
|---|---|---| |---|---|---|
| 2026-09-22 | **11차 F-5 뒤 (사용자 "응 고쳐줘")** — 켜자마자(또는 탭을 눌러 돌아오면) 키보드 포커스가 없거나 탭 줄에 있어, 코드 칸을 한 번 누르기 전까지 **모든 단축키**(Ctrl+S · F9 · Ctrl++ …)가 듣지 않았다. 파일 비교 화면이 뜰 때 · 돌아올 때 · 창이 활성이 될 때 포커스가 비었거나 화면 밖이면 편집기로 준다(화면 안 · 다른 창의 포커스는 그대로). E-1 병합 길 회귀 없음 | 아래 |
| 2026-09-22 | **11차 F-5 (추가 요청)** — 코드 칸 글자 크기 8~24(기본 12). 한 값을 모든 모니터에(사용자 결정 — 모니터마다 다른 Windows 배율은 플랫폼 PerMonitorV2 가 맞춤), 코드 칸만. `CodeFontMetrics` 가 줄 높이 · 번호 칸 · 여백 단추를 같은 비율로 내고(12 에서 지금 값 그대로) 화면이 DynamicResource 로 한 벌 갈아 끼움, `MergeLine.MinimumRowHeight` 도 따라감. Ctrl+휠 · Ctrl++/- · Ctrl+0 · 설정 "보기 > 코드 글자 크기", 상태 줄 `글자 N`. 줄 번호가 코드보다 몇 px 위에 뜨던 것(24 에서 커짐)도 같은 줄 높이 규칙으로 맞춤. 8 · 12 · 24 에서 네 칸 행 높이 같음. FontTest 110 · ShortcutTest +14(1,538건) | 아래 | | 2026-09-22 | **11차 F-5 (추가 요청)** — 코드 칸 글자 크기 8~24(기본 12). 한 값을 모든 모니터에(사용자 결정 — 모니터마다 다른 Windows 배율은 플랫폼 PerMonitorV2 가 맞춤), 코드 칸만. `CodeFontMetrics` 가 줄 높이 · 번호 칸 · 여백 단추를 같은 비율로 내고(12 에서 지금 값 그대로) 화면이 DynamicResource 로 한 벌 갈아 끼움, `MergeLine.MinimumRowHeight` 도 따라감. Ctrl+휠 · Ctrl++/- · Ctrl+0 · 설정 "보기 > 코드 글자 크기", 상태 줄 `글자 N`. 줄 번호가 코드보다 몇 px 위에 뜨던 것(24 에서 커짐)도 같은 줄 높이 규칙으로 맞춤. 8 · 12 · 24 에서 네 칸 행 높이 같음. FontTest 110 · ShortcutTest +14(1,538건) | 아래 |
| 2026-09-22 | **11차 F-4 (추가 요청)** — 파일 비교 오른쪽 ACTIONS 칸을 감추는 단추 · F9(사용자 결정). 감추면 칸과 구분선 폭을 0 으로 해 코드 칸이 넓어지고, 펴면 끌어 둔 폭 그대로. 설정 `ActionsHidden` 에 기억. 감춘 채 Ctrl+S · Alt+←/→ 그대로. **덧(사용자 요청)**: 아래 탭 줄 접기 — 플랫폼에 [탭 접기] · 띠 · F11 을 넣음(플랫폼 changelog 참고, 사용자 확인 뒤 배포 09-22 16:22). 1440p 150% 에서 코드 칸 폭 +163px · 높이 +71px. ShortcutTest +3 · 플랫폼 PlatformCfgTest +7 | 아래 | | 2026-09-22 | **11차 F-4 (추가 요청)** — 파일 비교 오른쪽 ACTIONS 칸을 감추는 단추 · F9(사용자 결정). 감추면 칸과 구분선 폭을 0 으로 해 코드 칸이 넓어지고, 펴면 끌어 둔 폭 그대로. 설정 `ActionsHidden` 에 기억. 감춘 채 Ctrl+S · Alt+←/→ 그대로. **덧(사용자 요청)**: 아래 탭 줄 접기 — 플랫폼에 [탭 접기] · 띠 · F11 을 넣음(플랫폼 changelog 참고, 사용자 확인 뒤 배포 09-22 16:22). 1440p 150% 에서 코드 칸 폭 +163px · 높이 +71px. ShortcutTest +3 · 플랫폼 PlatformCfgTest +7 | 아래 |
| 2026-09-22 | **11차 F-3 뒤 (사용자 발견)** — 탐색기에서 폴더 둘을 보내면 왼쪽 칸이 설정의 기본 저장소(`plain-compare`)로 덮여 비교가 "왼쪽만" 투성이였다. `FolderViewModel` 이 넘어온 요청을 받은 뒤 "열어 둔 저장소를 왼쪽 기본값으로" 가 덮어쓴 것(비교는 비동기라 덮인 값을 읽음) — 기본값을 먼저, 요청을 나중에. 켤 때 받은 인자를 파일 로그에 남김. 앞선 시험은 로그만 보고 칸 값을 제대로 읽지 않아 지나쳤다 | c35280a | | 2026-09-22 | **11차 F-3 뒤 (사용자 발견)** — 탐색기에서 폴더 둘을 보내면 왼쪽 칸이 설정의 기본 저장소(`plain-compare`)로 덮여 비교가 "왼쪽만" 투성이였다. `FolderViewModel` 이 넘어온 요청을 받은 뒤 "열어 둔 저장소를 왼쪽 기본값으로" 가 덮어쓴 것(비교는 비동기라 덮인 값을 읽음) — 기본값을 먼저, 요청을 나중에. 켤 때 받은 인자를 파일 로그에 남김. 앞선 시험은 로그만 보고 칸 값을 제대로 읽지 않아 지나쳤다 | c35280a |

View File

@ -374,7 +374,7 @@
| **완료** | F-2 메서드 단위로 골라 반영 — [이 메서드 반영](C# + C/C++, 클래스 멤버 전부 · 사용자 결정), 캐럿이 놓인 멤버를 반영해 올 쪽에서 중괄호 짝으로 잡음(`MemberRanges` — 문자열 · 주석 · raw · `#if` 갈래에 안 속음, 못 잡으면 단추 끄고 까닭 한 줄). 덩어리 · 메서드 반영은 주석 한 줄(메서드 이름 위) · 로그 한 줄 · Undo 한 번 — 덩어리가 줄마다 주석을 달던 것도 고침. 메서드 반영 = 같은 줄 줄 단위 반영 바이트 같음. MemberTest 83건(C# 29 · C++ 13 멤버) · 1,395건 | | **완료** | F-2 메서드 단위로 골라 반영 — [이 메서드 반영](C# + C/C++, 클래스 멤버 전부 · 사용자 결정), 캐럿이 놓인 멤버를 반영해 올 쪽에서 중괄호 짝으로 잡음(`MemberRanges` — 문자열 · 주석 · raw · `#if` 갈래에 안 속음, 못 잡으면 단추 끄고 까닭 한 줄). 덩어리 · 메서드 반영은 주석 한 줄(메서드 이름 위) · 로그 한 줄 · Undo 한 번 — 덩어리가 줄마다 주석을 달던 것도 고침. 메서드 반영 = 같은 줄 줄 단위 반영 바이트 같음. MemberTest 83건(C# 29 · C++ 13 멤버) · 1,395건 |
| **완료** | F-3 탐색기 [보내기] 로 폴더 두 개 바로 비교 — 탐색기는 여러 개를 프로세스 하나에 인자로 모두 넘김(일곱 경우 값으로 잼), 폴더 둘은 창 하나에서 폴더 비교까지. 사용자 결정대로 3개 이상은 앞의 둘 + 알림, 260자 넘는 경로(탐색기가 8.3 이름으로 넘김 · net48 이 못 찾음)는 열지 않고 알림, 옮긴 배포 묶음은 켤 때 보내기 바로가기를 저절로 고침(바로가기 파일만 · 레지스트리 안 씀), 떠 있을 때는 새 창 그대로. 설치 파일 필요 없음. ArgsTest +16 · 1,411건. **사용자가 실제 탐색기에서 찾은 것**: 보낸 왼쪽 폴더를 기본 저장소가 덮어써 "왼쪽만" 투성이 → 기본값을 먼저 두고 요청을 나중에 받게 고침, 켤 때 받은 인자를 파일 로그에 남김 | | **완료** | F-3 탐색기 [보내기] 로 폴더 두 개 바로 비교 — 탐색기는 여러 개를 프로세스 하나에 인자로 모두 넘김(일곱 경우 값으로 잼), 폴더 둘은 창 하나에서 폴더 비교까지. 사용자 결정대로 3개 이상은 앞의 둘 + 알림, 260자 넘는 경로(탐색기가 8.3 이름으로 넘김 · net48 이 못 찾음)는 열지 않고 알림, 옮긴 배포 묶음은 켤 때 보내기 바로가기를 저절로 고침(바로가기 파일만 · 레지스트리 안 씀), 떠 있을 때는 새 창 그대로. 설치 파일 필요 없음. ArgsTest +16 · 1,411건. **사용자가 실제 탐색기에서 찾은 것**: 보낸 왼쪽 폴더를 기본 저장소가 덮어써 "왼쪽만" 투성이 → 기본값을 먼저 두고 요청을 나중에 받게 고침, 켤 때 받은 인자를 파일 로그에 남김 |
| **완료** | F-4 ACTIONS 칸 감추기(단추 · F9, 끌어 둔 폭 기억, 감춰도 Ctrl+S · Alt+←/→ 그대로) + **덧: 아래 탭 줄 접기**(사용자 요청 — 플랫폼 [탭 접기] · 16 DIP 띠 · F11, 그 항목만 기억, 다른 소비 프로그램도 쓰되 기본 펼침). 1440p 150% 에서 코드 칸 폭 +163px · 높이 +71px. 플랫폼 배포함(09-22 16:22) | | **완료** | F-4 ACTIONS 칸 감추기(단추 · F9, 끌어 둔 폭 기억, 감춰도 Ctrl+S · Alt+←/→ 그대로) + **덧: 아래 탭 줄 접기**(사용자 요청 — 플랫폼 [탭 접기] · 16 DIP 띠 · F11, 그 항목만 기억, 다른 소비 프로그램도 쓰되 기본 펼침). 1440p 150% 에서 코드 칸 폭 +163px · 높이 +71px. 플랫폼 배포함(09-22 16:22) |
| **완료** | F-5 코드 글자 크기(8~24, 기본 12) — 한 값을 모든 모니터에(모니터 배율은 플랫폼), 코드 칸만, Ctrl+휠 · Ctrl++/- · Ctrl+0 + 설정 "보기 > 코드 글자 크기"(사용자 결정). 줄 높이 · 번호 칸 · 여백 단추가 같은 비율로(`CodeFontMetrics`, 12 에서 지금 값 그대로), 줄 번호 기준선을 코드와 맞춤. 8 · 12 · 24 에서 네 칸 행 높이 같음 · 단추 행 안. FontTest 110 · 1,538건. Ctrl+휠 · 키 · 껐다 켜도 남음 확인. 켜자마자는 포커스가 없어 단축키가 모두 안 듣는 것(원래 있던 것)을 찾음 | | **완료** | F-5 코드 글자 크기(8~24, 기본 12) — 한 값을 모든 모니터에(모니터 배율은 플랫폼), 코드 칸만, Ctrl+휠 · Ctrl++/- · Ctrl+0 + 설정 "보기 > 코드 글자 크기"(사용자 결정). 줄 높이 · 번호 칸 · 여백 단추가 같은 비율로(`CodeFontMetrics`, 12 에서 지금 값 그대로), 줄 번호 기준선을 코드와 맞춤. 8 · 12 · 24 에서 네 칸 행 높이 같음 · 단추 행 안. FontTest 110 · 1,538건. Ctrl+휠 · 키 · 껐다 켜도 남음 확인. 켜자마자는 포커스가 없어 단축키가 모두 안 듣던 것(원래 있던 것)도 고침 — 포커스가 비었거나 화면 밖이면 편집기로 |
| 대기 | F-6 글자를 되도록 많이 보이게 — 넓게 보기 한 단추, 늘어난 줄 수로 잼 | | 대기 | F-6 글자를 되도록 많이 보이게 — 넓게 보기 한 단추, 늘어난 줄 수로 잼 |
| 대기 | F-7 어느 모니터에서든 제대로 뜨게 — 증상부터 적고 플랫폼 영향 확인 | | 대기 | F-7 어느 모니터에서든 제대로 뜨게 — 증상부터 적고 플랫폼 영향 확인 |
| 대기 | F-8 저장한 뒤에도 되돌리기 (지금은 저장 네 길이 스택을 비움) | | 대기 | F-8 저장한 뒤에도 되돌리기 (지금은 저장 네 길이 스택을 비움) |

View File

@ -251,6 +251,7 @@ namespace MergeHub.Views
IsVisibleChanged += (_, e) => IsVisibleChanged += (_, e) =>
{ {
if (e.NewValue is true && DataContext is HomeViewModel vm) vm.RecompareIfSettingsChanged(); if (e.NewValue is true && DataContext is HomeViewModel vm) vm.RecompareIfSettingsChanged();
if (e.NewValue is true) FocusEditorWhenIdle();
}; };
// 두 패널은 같은 이동값을 쓴다. LOCAL 만 밀면 BASE 와 줄이 어긋나 보인다 - // 두 패널은 같은 이동값을 쓴다. LOCAL 만 밀면 BASE 와 줄이 어긋나 보인다 -
// 나란히 놓고 보는 도구에서 이건 곧바로 눈에 띈다. // 나란히 놓고 보는 도구에서 이건 곧바로 눈에 띈다.
@ -271,7 +272,18 @@ namespace MergeHub.Views
// 글꼴·배율·창 크기가 바뀌면 다시 재서 맞춘다. // 글꼴·배율·창 크기가 바뀌면 다시 재서 맞춘다.
LayoutUpdated += (_, _) => AlignDecorationToEditor(); LayoutUpdated += (_, _) => AlignDecorationToEditor();
Loaded += (_, _) => HookHorizontalWheel(); Loaded += (_, _) =>
{
HookHorizontalWheel();
FocusEditorWhenIdle();
// 켤 때는 사용자 이름 창이 먼저 떠 본 창이 늦게 활성이 된다. 그때도 한 번 더 본다.
if (Window.GetWindow(this) is { } window && !_watchingActivation)
{
_watchingActivation = true;
window.Activated += (_, _) => { if (IsVisible) FocusEditorWhenIdle(); };
}
};
Unloaded += (_, _) => UnhookHorizontalWheel(); Unloaded += (_, _) => UnhookHorizontalWheel();
} }
@ -504,6 +516,35 @@ namespace MergeHub.Views
Dispatcher.BeginInvoke(new Action(MatchTextWidths), System.Windows.Threading.DispatcherPriority.Loaded); Dispatcher.BeginInvoke(new Action(MatchTextWidths), System.Windows.Threading.DispatcherPriority.Loaded);
} }
private bool _watchingActivation;
/// <summary>
/// 키보드 포커스가 비어 있거나 이 화면 밖(아래 탭 줄 등)에 있으면 편집기로 준다(11차 F-5 에서 찾음).
///
/// 켜자마자는 포커스가 아무 데도 없어 단축키(Ctrl+S · F9 · Ctrl++ …)가 코드 칸을 한 번 누르기 전까지
/// 하나도 듣지 않았다 - 단축키는 이 화면의 PreviewKeyDown 이 받기 때문이다. 탭을 눌러 돌아와도
/// 포커스가 탭 줄에 남아 같았다.
/// 이 화면 안(찾기 칸 · 커밋 메시지 칸 등)이나 다른 창(대화상자)에 있는 포커스는 건드리지 않는다.
/// </summary>
private void FocusEditorWhenIdle() =>
Dispatcher.BeginInvoke(new Action(() =>
{
if (!IsVisible || !LocalEditor.IsVisible) return;
var window = Window.GetWindow(this);
if (window is null) return;
if (Keyboard.FocusedElement is DependencyObject focused)
{
if (IsAncestorOf(focused)) return;
if (!ReferenceEquals(Window.GetWindow(focused), window)) return;
}
// 창이 아직 활성이 아니면(이름 창이 떠 있을 때) 논리 포커스만 정해 둔다 - 활성이 되면 WPF 가 거기로 돌려준다.
FocusManager.SetFocusedElement(window, LocalEditor);
if (window.IsActive) LocalEditor.Focus();
}), DispatcherPriority.Input);
/// <summary>감추기 전 ACTIONS 칸 폭. 구분선을 끌어 바꿔 뒀으면 그 폭으로 돌아온다.</summary> /// <summary>감추기 전 ACTIONS 칸 폭. 구분선을 끌어 바꿔 뒀으면 그 폭으로 돌아온다.</summary>
private GridLength _actionsWidth = new GridLength(214); private GridLength _actionsWidth = new GridLength(214);