파일 계열 키바인딩 배선 + 미저장 종료 가드
메뉴에는 Ctrl+N/O/S/P 가 표시되지만 Window.InputBindings 가 아예 없어 실제로는 동작하지 않는 잘못된 어포던스였다. 그리고 MainView 에 Closing 핸들러가 없어 타이틀바 X·Alt+F4· 파일 종료 어느 경로로도 미저장 확인이 뜨지 않아, 미저장 다중 문서가 확인 없이 버려졌다. - UndoService 에 savedDepth 기반 IsDirty/MarkSaved 추가. 기존 CanUndo 는 저장해도 영구 더티라 종료마다 무의미한 확인이 떠서 가드가 존재 가치를 잃는다. Undo/Redo 로 저장 지점 깊이에 되돌아오면 자동 clean 이 되고, 저장 지점보다 얕은 상태에서 새 편집이 들어오면 (분기) 되돌아갈 수 없으므로 savedDepth=-1 로 영구 더티 처리한다. 용량 트림 시에도 보정. - OnSaveFile/OnSaveToDb 를 bool 반환으로. 기존 void 는 저장 대화상자 취소를 감지할 수 없어 종료 가드에서 저장 선택지를 줄 수 없었다. 성공 시 MarkSaved() 호출. - ConfirmCloseAll() — 문서별 YesNoCancel 확인. 확인 직전 해당 문서를 활성 탭으로 올려 어느 문서를 묻는지 보이게 한다(저장 경로가 CurrentDesigner 대상이라 전환이 필수다). 과거 버전 열람 탭은 저장하면 활성 디자인을 과거 내용으로 덮으므로 저장 선택지를 주지 않는다. - 종료 경로 두 개를 모두 막는다: MainView.Closing 에서 e.Cancel, 그리고 파일 종료의 Application.Shutdown() 은 e.Cancel 을 무시하므로 VM 이 먼저 확인하고 IsShuttingDown 을 세워 Closing 이 재질문하지 않게 한다. - Window.InputBindings 에는 파일/앱 계열만(Ctrl+N/O/S/Shift+S/P/W). 편집 계열은 CanvasKeyboardBehavior 가 계속 담당한다 — Ctrl+Z/C/V/X/A/D/G 를 창에 올리면 인스펙터· 검색 상자 TextBox 의 표준 편집 키와 충돌하고 Spread 붙여넣기 가드도 우회할 여지가 생긴다. - 저장 진입부에서 포커스가 머문 TextBox 의 BindingExpression.UpdateSource() 호출. 인스펙터 바인딩이 UpdateSourceTrigger=LostFocus 라 값을 고치다 바로 Ctrl+S 를 누르면 커밋되지 않은 채 저장되어 조용히 유실됐다. 검증: edit-smoke 에 더티 추적 8건 추가(초기 clean / 편집 후 dirty / MarkSaved 후 clean / 저장 지점 Undo 복귀 시 clean / 더 되돌리면 dirty / Redo 복귀 시 clean / 분기 후 영구 dirty). 1단계 전체 회귀 통과 — 테스트 70/70, edit-smoke 실패 0, 왕복 1,271건 diff 0/예외 0, db-save-smoke 제자리 갱신(P163)·버저닝(S999) 양쪽, db-word-smoke 통과. 주의: 키바인딩과 종료 대화상자의 실 UI 조작 확인은 아직 하지 않았다(무인 검증 불가 구간). 병행 운영 전 수동 확인 항목으로 남긴다. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
3dcac31a5b
commit
5c97bf1440
@@ -16,6 +16,9 @@ public sealed class UndoService
|
||||
private readonly Func<FormDocument> getDocument;
|
||||
private readonly Action<FormDocument> restoreDocument;
|
||||
private DateTime lastNudgeAt = DateTime.MinValue;
|
||||
|
||||
/// <summary>마지막 저장 시점의 undo 깊이 — -1 은 저장 지점이 소실되어 항상 더티</summary>
|
||||
private int savedDepth;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
@@ -24,6 +27,13 @@ public sealed class UndoService
|
||||
|
||||
/// <summary>Redo 가능 여부</summary>
|
||||
public bool CanRedo => redoStack.Count > 0;
|
||||
|
||||
/// <summary>
|
||||
/// 저장하지 않은 변경이 있는지. CanUndo 와 달리 저장 이후 편집이 없으면 false 다
|
||||
/// (CanUndo 만 쓰면 저장 직후에도 영구히 더티라 종료 확인이 무의미해진다).
|
||||
/// Undo/Redo 로 저장 지점 깊이에 되돌아오면 자동으로 clean 이 된다.
|
||||
/// </summary>
|
||||
public bool IsDirty => savedDepth < 0 || undoStack.Count != savedDepth;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
@@ -44,14 +54,29 @@ public sealed class UndoService
|
||||
public void Snapshot()
|
||||
{
|
||||
undoStack.Add(getDocument().Clone());
|
||||
|
||||
// 저장 지점보다 얕은 상태에서 새 편집이 들어오면(=Undo 후 편집) 그 지점으로는 되돌아갈 수 없다
|
||||
if (savedDepth > undoStack.Count - 1)
|
||||
{
|
||||
savedDepth = -1;
|
||||
}
|
||||
|
||||
if (undoStack.Count > Capacity)
|
||||
{
|
||||
undoStack.RemoveRange(0, undoStack.Count - Capacity);
|
||||
var trimmed = undoStack.Count - Capacity;
|
||||
undoStack.RemoveRange(0, trimmed);
|
||||
if (savedDepth >= 0)
|
||||
{
|
||||
savedDepth = savedDepth >= trimmed ? savedDepth - trimmed : -1;
|
||||
}
|
||||
}
|
||||
redoStack.Clear();
|
||||
lastNudgeAt = DateTime.MinValue;
|
||||
}
|
||||
|
||||
/// <summary>저장 완료 시점 표시 — 이후 편집이 없으면 <see cref="IsDirty"/> 가 false</summary>
|
||||
public void MarkSaved() => savedDepth = undoStack.Count;
|
||||
|
||||
/// <summary>넛지(방향키) 스냅샷 — 400ms 이내 연속 입력은 1스텝으로 코얼레스</summary>
|
||||
public void SnapshotForNudge()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user