4차 B-1: 최근 비교 목록
파일 짝과 폴더 짝을 따로 15개까지 기억한다. 탭으로 나눈 글로 설정 폴더에 둔다. 없는 경로는 흐리게 보여 주고 지우지 않는다. 검사 33건 추가.
This commit is contained in:
parent
0625236fc3
commit
b9456b7f14
@ -157,3 +157,43 @@ MergeHub 파일 비교 결과
|
||||
화면의 머지 주석 미리보기가 `// [김승준] 2026-09-10` 이었으니 **두 이름이 같다.**
|
||||
|
||||
검사 8건 추가(`ReportTest` 23 → 31). 전체 **522건 통과** · 빌드 경고 0 · 오류 0.
|
||||
---
|
||||
|
||||
## B-1. 최근 비교 목록 — **완료** (2026-09-10)
|
||||
|
||||
**왜** - 같은 짝을 반복해서 여는데 그때마다 경로를 다시 골라야 했다. 매일 쓰는 사람에게 가장 잦은 마찰이다.
|
||||
|
||||
`Services/RecentPairs.cs` 신설. 화면 없이 검사할 수 있게 **기억하는 일만** 여기서 한다.
|
||||
|
||||
**파일 짝과 폴더 짝을 따로 둔다.** 한 목록에 섞으면 파일 비교 화면에서 폴더가 나온다.
|
||||
상한은 15개 - 목록이 길면 고르는 것이 더 느려진다.
|
||||
|
||||
**저장 형식은 탭으로 나눈 글이다.** JSON 을 안 쓴 까닭 - 줄 몇 개가 전부라 라이브러리를 하나 더 매달 값이 없고,
|
||||
글이면 사람이 열어 보고 손으로 고칠 수도 있다. 윈도우 경로에는 탭이 못 들어가서 나누는 데 문제가 없다.
|
||||
자리는 `D:\UbisamConfig\Settings\MergeHub.Recent.txt` 다. **설정 화면에는 안 내놓는다** -
|
||||
사람이 손으로 고칠 값이 아니고 설정 목록만 어지럽힌다.
|
||||
|
||||
**켤 때 한 번 읽고 바뀔 때마다 바로 쓴다.** 프로그램이 갑자기 꺼져도 목록이 남게 하려는 것이다.
|
||||
|
||||
**없는 경로를 자동으로 지우지 않는다.** 지시서가 그렇게 못박았다 - 네트워크 드라이브나 USB 는
|
||||
잠깐 안 보일 뿐인데 지워 버리면 다시 찾아 들어가야 한다. 흐리게 보여 주고 열려고 할 때 알린다.
|
||||
|
||||
**비교가 끝난 짝만 담는다.** 읽다 실패했거나 취소한 짝을 남기면 다음에 골랐을 때 또 실패한다.
|
||||
|
||||
**목록 이름은 양쪽 끝 이름만** 쓴다. 전체 경로는 길어서 목록에서 읽히지 않는다.
|
||||
두 이름이 같으면 하나만 적고(`Other.cpp`), 다르면 둘 다 적는다(`기준.cs ↔ 내작업.cs`). 전체 경로는 툴팁에 있다.
|
||||
|
||||
여는 길은 다른 데와 같은 `RequestPlainCompare` 다. 그래서 **저장하지 않은 편집도 A-1 에서 함께 걸린다.**
|
||||
|
||||
**화면에서 확인한 것**
|
||||
|
||||
| 한 일 | 결과 |
|
||||
|---|---|
|
||||
| 파일 비교 · 폴더 비교를 한 번씩 | `MergeHub.Recent.txt` 에 `F` 줄과 `D` 줄이 하나씩 |
|
||||
| 프로그램을 껐다 켜기 | 두 목록이 그대로 남았다 |
|
||||
| 폴더 최근에서 고르기 | 두 칸이 채워지고 비교가 바로 돌았다 (`전체 2개 · 다름 2`) |
|
||||
| 파일 최근에서 고르기 | `BASE · Weld.cpp` / `LOCAL · Weld.cpp` 로 열렸다 |
|
||||
| 짝의 두 파일을 지운 뒤 | 목록에 그대로 남고 **흐리게** 보였다 |
|
||||
| 그 죽은 짝을 고르기 | `두 파일 모두 찾을 수 없습니다` 토스트, 화면은 그대로 |
|
||||
|
||||
검사 33건 추가(`RecentTest`). 전체 **555건 통과** · 빌드 경고 0 · 오류 0.
|
||||
@ -8,6 +8,7 @@
|
||||
|
||||
| 날짜 | 항목 | 커밋 |
|
||||
|---|---|---|
|
||||
| 2026-09-10 | **4차 B-1** — 최근에 견준 파일 짝·폴더 짝을 기억해 위 줄에서 바로 연다. 없는 경로는 흐리게만 하고 지우지 않는다 | 아래 |
|
||||
| 2026-09-10 | **4차 A-2** — 내보낸 보고서 머리글에 만든 사람을 넣음. 이름 판단을 머지 주석과 한 함수로 모았다 | 아래 |
|
||||
| 2026-09-10 | **4차 A-1** — 저장하지 않은 편집이 말없이 사라지던 길 여덟 군데를 막음. 플랫폼에 세 갈래 확인 창을 새로 넣었다 | 아래 |
|
||||
| 2026-09-10 | **4차 D-2** — 항목을 끝내면 상세·상태·한 줄을 각각 어디에 적을지 공통 규칙으로 못박음 | 아래 |
|
||||
@ -29,6 +30,7 @@
|
||||
|
||||
| 날짜 | 문서 | 내용 |
|
||||
|---|---|---|
|
||||
| 2026-09-10 | **4차 B-1** — 최근에 견준 파일 짝·폴더 짝을 기억해 위 줄에서 바로 연다. 없는 경로는 흐리게만 하고 지우지 않는다 | 아래 |
|
||||
| 2026-09-10 | **4차 A-2** — 내보낸 보고서 머리글에 만든 사람을 넣음. 이름 판단을 머지 주석과 한 함수로 모았다 | 아래 |
|
||||
| 2026-09-10 | **4차 A-1** — 저장하지 않은 편집이 말없이 사라지던 길 여덟 군데를 막음. 플랫폼에 세 갈래 확인 창을 새로 넣었다 | 아래 |
|
||||
| 2026-09-10 | **4차 D-2** — 항목을 끝내면 상세·상태·한 줄을 각각 어디에 적을지 공통 규칙으로 못박음 | 아래 |
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
|
||||
| 상태 | 항목 |
|
||||
|---|---|
|
||||
| 대기 | B-1 최근 비교 목록 |
|
||||
| **완료** | B-1 최근 비교 목록 |
|
||||
| 대기 | B-2 같은 줄 접기 |
|
||||
| 대기 | B-3 폴더 비교에서 다음 다른 파일로 |
|
||||
| 대기 | B-4 외부 편집기로 열기 |
|
||||
|
||||
@ -48,6 +48,11 @@ namespace MergeHub.Services
|
||||
// 그러면 주석에 등록한 이름 대신 윈도우 계정이 찍힌다.
|
||||
AuthorName = ResolveAuthorName();
|
||||
|
||||
// 최근 목록은 켤 때 한 번 읽고, 바뀔 때마다 바로 쓴다.
|
||||
// 프로그램이 갑자기 꺼져도 목록이 남게 하려는 것이다 - 몇 줄짜리 글이라 쓰는 값이 싸다.
|
||||
Recent.Load(RecentPath);
|
||||
Recent.Changed += (_, _) => Recent.Save(RecentPath);
|
||||
|
||||
// `git mergetool` · 탐색기 우클릭이 넘긴 것을 화면으로 보낸다.
|
||||
// 여기서 하는 이유 - 이 객체는 첫 화면이 만들어질 때 함께 만들어지므로
|
||||
// 요청을 남겨 둘 자리로 가장 이르다.
|
||||
@ -71,6 +76,21 @@ namespace MergeHub.Services
|
||||
|
||||
public IGitService Git { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 최근에 견준 짝. 파일 비교와 폴더 비교가 함께 본다.
|
||||
///
|
||||
/// 여기에 두는 까닭 - 두 화면이 서로를 모르는 채로 같은 목록을 봐야 하고,
|
||||
/// 이 객체가 이미 그 통로 노릇을 하고 있다.
|
||||
/// </summary>
|
||||
public RecentPairs Recent { get; } = new RecentPairs();
|
||||
|
||||
/// <summary>
|
||||
/// 최근 목록을 두는 자리. 플랫폼 설정 폴더 아래에 둔다 - 장비마다 한 자리에 모인다.
|
||||
/// <b>설정 화면에는 내놓지 않는다.</b> 사람이 손으로 고칠 값이 아니고, 설정 목록만 어지럽힌다.
|
||||
/// </summary>
|
||||
private static string RecentPath => Path.Combine(
|
||||
UbisamBase.Core.Bootstrap.UbisamAppBuilder.ConfigRoot, "Settings", "MergeHub.Recent.txt");
|
||||
|
||||
public MergeHubSettings Settings { get; }
|
||||
|
||||
/// <summary>저장소 상태가 다시 읽힐 때마다 발생. 화면들이 자기 패널을 갱신한다.</summary>
|
||||
|
||||
195
src/MergeHub/Services/RecentPairs.cs
Normal file
195
src/MergeHub/Services/RecentPairs.cs
Normal file
@ -0,0 +1,195 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace MergeHub.Services
|
||||
{
|
||||
/// <summary>최근 목록에 담기는 것이 파일 짝인지 폴더 짝인지.</summary>
|
||||
public enum RecentKind
|
||||
{
|
||||
Files,
|
||||
Folders
|
||||
}
|
||||
|
||||
/// <summary>최근에 견준 짝 하나.</summary>
|
||||
public sealed class RecentPair
|
||||
{
|
||||
public RecentPair(RecentKind kind, string left, string right, DateTime when)
|
||||
{
|
||||
Kind = kind;
|
||||
Left = left ?? string.Empty;
|
||||
Right = right ?? string.Empty;
|
||||
When = when;
|
||||
}
|
||||
|
||||
public RecentKind Kind { get; }
|
||||
public string Left { get; }
|
||||
public string Right { get; }
|
||||
public DateTime When { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 목록에 뿌릴 이름. <b>양쪽 끝 이름만</b> 보여 준다 - 전체 경로는 길어서 목록에서 읽히지 않고,
|
||||
/// 어느 짝인지 가르는 것은 대개 파일 이름이다. 전체 경로는 툴팁에 있다.
|
||||
/// </summary>
|
||||
public string Label
|
||||
{
|
||||
get
|
||||
{
|
||||
var l = Tail(Left);
|
||||
var r = Tail(Right);
|
||||
return string.Equals(l, r, StringComparison.OrdinalIgnoreCase) ? l : $"{l} ↔ {r}";
|
||||
}
|
||||
}
|
||||
|
||||
public string Tip => $"{Left}{Environment.NewLine}{Right}";
|
||||
|
||||
/// <summary>
|
||||
/// 양쪽이 다 있는지. <b>없다고 목록에서 지우지는 않는다</b> -
|
||||
/// 네트워크 드라이브나 USB 는 잠깐 안 보일 뿐인데 지워 버리면 다시 찾아 들어가야 한다.
|
||||
/// 흐리게 보여 주고, 열려고 할 때 알린다.
|
||||
/// </summary>
|
||||
public bool Exists => Kind == RecentKind.Folders
|
||||
? Directory.Exists(Left) && Directory.Exists(Right)
|
||||
: File.Exists(Left) || File.Exists(Right);
|
||||
|
||||
private static string Tail(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path)) return "-";
|
||||
|
||||
var trimmed = path.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
var name = Path.GetFileName(trimmed);
|
||||
|
||||
// 드라이브 뿌리(C:\)는 이름이 비어 나온다. 그때는 경로를 그대로 쓴다.
|
||||
return name.Length > 0 ? name : trimmed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 최근에 견준 짝을 기억한다.
|
||||
///
|
||||
/// 같은 짝을 반복해서 여는 일이 잦은데 그때마다 경로를 다시 골라야 했다.
|
||||
/// 파일 짝과 폴더 짝을 <b>따로</b> 둔다 - 한 목록에 섞으면 파일 비교 화면에서 폴더가 나온다.
|
||||
///
|
||||
/// 저장 형식은 탭으로 나눈 글이다. JSON 을 쓰지 않는 까닭 - 이 목록은 줄 몇 개가 전부라
|
||||
/// 라이브러리를 하나 더 매달 값이 없고, 글이면 사람이 열어 보고 손으로 고칠 수도 있다.
|
||||
/// 윈도우 경로에는 탭이 들어갈 수 없어 나누는 데 문제가 없다.
|
||||
/// </summary>
|
||||
public sealed class RecentPairs
|
||||
{
|
||||
/// <summary>이보다 많이 쌓지 않는다. 목록이 길면 고르는 것이 더 느려진다.</summary>
|
||||
public const int Max = 15;
|
||||
|
||||
private readonly List<RecentPair> _files = new List<RecentPair>();
|
||||
private readonly List<RecentPair> _folders = new List<RecentPair>();
|
||||
|
||||
public IReadOnlyList<RecentPair> Files => _files;
|
||||
public IReadOnlyList<RecentPair> Folders => _folders;
|
||||
|
||||
/// <summary>목록이 바뀌었다. 화면이 다시 그리도록.</summary>
|
||||
public event EventHandler? Changed;
|
||||
|
||||
public void Add(RecentKind kind, string left, string right, DateTime when)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(left) && string.IsNullOrWhiteSpace(right)) return;
|
||||
|
||||
var list = kind == RecentKind.Folders ? _folders : _files;
|
||||
|
||||
// 같은 짝이 이미 있으면 지우고 맨 위에 다시 넣는다. 최근 것이 위여야 한다.
|
||||
list.RemoveAll(p => Same(p, left, right));
|
||||
list.Insert(0, new RecentPair(kind, left ?? string.Empty, right ?? string.Empty, when));
|
||||
|
||||
while (list.Count > Max) list.RemoveAt(list.Count - 1);
|
||||
|
||||
Changed?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void Add(RecentKind kind, string left, string right) => Add(kind, left, right, DateTime.Now);
|
||||
|
||||
private static bool Same(RecentPair pair, string? left, string? right) =>
|
||||
string.Equals(pair.Left, left ?? string.Empty, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(pair.Right, right ?? string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
// ================= 글로 옮기기 =================
|
||||
|
||||
private const string Header = "# MergeHub 최근 비교 목록";
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
var text = new StringBuilder();
|
||||
text.AppendLine(Header);
|
||||
|
||||
foreach (var pair in _files) Write(text, pair);
|
||||
foreach (var pair in _folders) Write(text, pair);
|
||||
|
||||
return text.ToString();
|
||||
}
|
||||
|
||||
private static void Write(StringBuilder text, RecentPair pair) =>
|
||||
text.AppendLine(string.Join("\t",
|
||||
pair.Kind == RecentKind.Folders ? "D" : "F",
|
||||
pair.When.ToString("yyyy-MM-ddTHH:mm:ss"),
|
||||
pair.Left,
|
||||
pair.Right));
|
||||
|
||||
/// <summary>
|
||||
/// 글에서 되읽는다. <b>알아볼 수 없는 줄은 조용히 건너뛴다</b> -
|
||||
/// 최근 목록 하나 때문에 프로그램이 안 켜지면 안 된다.
|
||||
/// </summary>
|
||||
public void Parse(string? content)
|
||||
{
|
||||
_files.Clear();
|
||||
_folders.Clear();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(content)) { Changed?.Invoke(this, EventArgs.Empty); return; }
|
||||
|
||||
foreach (var raw in content!.Replace("\r\n", "\n").Split('\n'))
|
||||
{
|
||||
var line = raw.TrimEnd();
|
||||
if (line.Length == 0 || line.StartsWith("#", StringComparison.Ordinal)) continue;
|
||||
|
||||
var parts = line.Split('\t');
|
||||
if (parts.Length < 4) continue;
|
||||
|
||||
var kind = parts[0] == "D" ? RecentKind.Folders : RecentKind.Files;
|
||||
if (!DateTime.TryParse(parts[1], out var when)) when = DateTime.MinValue;
|
||||
|
||||
var list = kind == RecentKind.Folders ? _folders : _files;
|
||||
if (list.Count >= Max) continue;
|
||||
|
||||
list.Add(new RecentPair(kind, parts[2], parts[3], when));
|
||||
}
|
||||
|
||||
Changed?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
// ================= 파일 =================
|
||||
|
||||
public void Load(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
Parse(File.Exists(path) ? File.ReadAllText(path, Encoding.UTF8) : null);
|
||||
}
|
||||
catch (IOException) { Parse(null); }
|
||||
catch (UnauthorizedAccessException) { Parse(null); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 저장에 실패해도 조용히 넘어간다. 최근 목록은 편의 기능이라
|
||||
/// 이것 때문에 하던 일이 막히면 안 된다.
|
||||
/// </summary>
|
||||
public void Save(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dir = Path.GetDirectoryName(path);
|
||||
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) Directory.CreateDirectory(dir!);
|
||||
|
||||
File.WriteAllText(path, Serialize(), new UTF8Encoding(true));
|
||||
}
|
||||
catch (IOException) { }
|
||||
catch (UnauthorizedAccessException) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -121,6 +121,8 @@ namespace MergeHub.ViewModels
|
||||
|
||||
// 탐색기 우클릭으로 켰으면 그 폴더가 왼쪽에 들어온다.
|
||||
_workspace.FolderCompareRequested += OnFolderCompareRequested;
|
||||
_workspace.Recent.Changed += (_, _) => _workspace.OnUi(RefreshRecent);
|
||||
RefreshRecent();
|
||||
_workspace.DeliverPendingFolderCompare();
|
||||
|
||||
// 저장소를 열어 뒀으면 왼쪽 기본값으로 삼는다 - 대개 그 폴더를 견주게 된다.
|
||||
@ -151,6 +153,36 @@ namespace MergeHub.ViewModels
|
||||
|
||||
public ObservableCollection<FolderNode> Nodes { get; } = new ObservableCollection<FolderNode>();
|
||||
|
||||
// ================= 최근 비교 목록 =================
|
||||
|
||||
/// <summary>최근에 견준 폴더 짝. 파일 짝과 따로 둔다.</summary>
|
||||
public ObservableCollection<RecentPair> RecentFolders { get; } = new ObservableCollection<RecentPair>();
|
||||
|
||||
private void RefreshRecent()
|
||||
{
|
||||
RecentFolders.Clear();
|
||||
foreach (var pair in _workspace.Recent.Folders) RecentFolders.Add(pair);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 최근 목록에서 고른 짝으로 곧바로 견준다.
|
||||
/// 자리를 옮겼거나 지워진 짝이라도 목록에서 지우지 않는다 - 왜 못 여는지만 알려 준다.
|
||||
/// </summary>
|
||||
public void OpenRecent(RecentPair? pair)
|
||||
{
|
||||
if (pair is null) return;
|
||||
|
||||
if (!Directory.Exists(pair.Left) || !Directory.Exists(pair.Right))
|
||||
{
|
||||
MessageUtil.ShowWarningToast("폴더를 찾을 수 없습니다. 자리를 옮겼거나 지워진 것 같습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
LeftFolder = pair.Left;
|
||||
RightFolder = pair.Right;
|
||||
_ = CompareAsync();
|
||||
}
|
||||
|
||||
[ObservableProperty] private string _leftFolder = string.Empty;
|
||||
[ObservableProperty] private string _rightFolder = string.Empty;
|
||||
|
||||
@ -325,6 +357,9 @@ namespace MergeHub.ViewModels
|
||||
Rebuild();
|
||||
Summary = result.Summary;
|
||||
_workspace.AppendLog($"폴더 비교: {left} ↔ {right} · {result.Summary}");
|
||||
|
||||
// 비교가 끝난 짝만 담는다. 취소했거나 실패한 짝은 최근에 남길 값이 없다.
|
||||
_workspace.Recent.Add(RecentKind.Folders, left, right);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
|
||||
@ -37,8 +37,11 @@ namespace MergeHub.ViewModels
|
||||
_workspace.Refreshed += OnWorkspaceRefreshed;
|
||||
_workspace.StateChanged += RaiseCanExecuteChanged;
|
||||
_workspace.PlainCompareRequested += OnPlainCompareRequested;
|
||||
_workspace.Recent.Changed += (_, _) => _workspace.OnUi(RefreshRecent);
|
||||
_reflowTimer.Tick += (_, _) => Reflow();
|
||||
|
||||
RefreshRecent();
|
||||
|
||||
LoadRules();
|
||||
IsGitMode = _workspace.IsRepositoryOpen;
|
||||
RefreshRemoteBranches();
|
||||
@ -187,6 +190,37 @@ namespace MergeHub.ViewModels
|
||||
/// <summary>비교 대상 파일 목록(충돌 중이면 충돌 파일, 아니면 선택 커밋의 파일).</summary>
|
||||
public ObservableCollection<string> Files { get; } = new ObservableCollection<string>();
|
||||
|
||||
// ================= 최근 비교 목록 =================
|
||||
|
||||
/// <summary>최근에 견준 파일 짝. 위 줄의 목록에 뿌린다.</summary>
|
||||
public ObservableCollection<RecentPair> RecentFiles { get; } = new ObservableCollection<RecentPair>();
|
||||
|
||||
private void RefreshRecent()
|
||||
{
|
||||
RecentFiles.Clear();
|
||||
foreach (var pair in _workspace.Recent.Files) RecentFiles.Add(pair);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 최근 목록에서 고른 짝을 연다.
|
||||
///
|
||||
/// 자리를 옮겼거나 지워진 짝이라도 <b>목록에서 지우지 않는다</b> - 네트워크 드라이브나 USB 는
|
||||
/// 잠깐 안 보일 뿐이다. 대신 왜 못 여는지 알려 준다.
|
||||
/// 여는 길은 다른 데와 같다(<c>RequestPlainCompare</c>) - 저장하지 않은 편집도 그 길에서 함께 걸린다.
|
||||
/// </summary>
|
||||
public void OpenRecent(RecentPair? pair)
|
||||
{
|
||||
if (pair is null) return;
|
||||
|
||||
if (!File.Exists(pair.Left) && !File.Exists(pair.Right))
|
||||
{
|
||||
MessageUtil.ShowWarningToast("두 파일 모두 찾을 수 없습니다. 자리를 옮겼거나 지워진 것 같습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
_workspace.RequestPlainCompare(pair.Left, pair.Right);
|
||||
}
|
||||
|
||||
[ObservableProperty] private string? _selectedFile;
|
||||
[ObservableProperty] private MergeLine? _selectedLine;
|
||||
[ObservableProperty] private string _baseHeader = "BASE";
|
||||
@ -1728,6 +1762,10 @@ namespace MergeHub.ViewModels
|
||||
RefreshDiffPosition();
|
||||
RaiseCanExecuteChanged();
|
||||
|
||||
// 다 읽고 나서 최근 목록에 담는다. 읽다 실패한 짝을 목록에 남기면
|
||||
// 다음에 골랐을 때 또 실패한다.
|
||||
_workspace.Recent.Add(RecentKind.Files, LeftFile, RightFile);
|
||||
|
||||
// 줄을 고르는 것만으로는 화면이 따라오지 않는다. 캐럿까지 옮겨야 굴러간다.
|
||||
if (asked >= 0) GoToRow(asked);
|
||||
});
|
||||
|
||||
@ -97,6 +97,8 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="0" Content="왼쪽 폴더..." Command="{Binding PickLeftFolderCommand}"
|
||||
@ -112,7 +114,32 @@
|
||||
AllowDrop="True" PreviewDragOver="View_DragOver" PreviewDrop="RightFolder_Drop"
|
||||
FontFamily="Consolas" FontSize="11" Height="28" VerticalContentAlignment="Center"
|
||||
ToolTip="견줄 폴더"/>
|
||||
</Grid>
|
||||
|
||||
<TextBlock Grid.Column="4" Text="최근" VerticalAlignment="Center" Style="{StaticResource Muted}"
|
||||
FontSize="11" Margin="14,0,4,0"/>
|
||||
<ComboBox x:Name="RecentBox" ItemsSource="{Binding RecentFolders}"
|
||||
SelectionChanged="Recent_SelectionChanged"
|
||||
Width="200" Height="28" FontSize="11" Grid.Column="5" Margin="0"
|
||||
VerticalContentAlignment="Center"
|
||||
ToolTip="최근에 견준 짝. 고르면 바로 엽니다">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<!-- 자리를 옮겼거나 지워진 짝은 흐리게. 목록에서 지우지는 않는다 -
|
||||
네트워크 드라이브나 USB 는 잠깐 안 보일 뿐이다. -->
|
||||
<TextBlock Text="{Binding Label}" ToolTip="{Binding Tip}" FontSize="11">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Exists}" Value="False">
|
||||
<Setter Property="Opacity" Value="0.45"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox> </Grid>
|
||||
</Border>
|
||||
|
||||
<!-- ② 비교 방식 · 필터 -->
|
||||
|
||||
@ -52,6 +52,18 @@ namespace MergeHub.Views
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 최근 목록에서 하나를 고르면 그 짝으로 곧바로 견준다.
|
||||
/// 고른 뒤 바로 선택을 지운다 - 같은 짝을 다시 고를 수 있어야 한다.
|
||||
/// </summary>
|
||||
private void Recent_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (sender is not ComboBox box || box.SelectedItem is not Services.RecentPair pair) return;
|
||||
|
||||
box.SelectedIndex = -1;
|
||||
if (DataContext is FolderViewModel vm) vm.OpenRecent(pair);
|
||||
}
|
||||
|
||||
/// <summary>찾기 칸에서 Enter 를 누르면 바로 찾는다. 단추까지 가는 걸음을 줄인다.</summary>
|
||||
private void SearchBox_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
|
||||
@ -396,6 +396,32 @@
|
||||
ToolTip="{Binding RightFile}"
|
||||
Background="Transparent"
|
||||
AllowDrop="True" DragOver="View_DragOver" Drop="RightFile_Drop"/>
|
||||
|
||||
<TextBlock Text="최근" VerticalAlignment="Center" Style="{StaticResource Muted}"
|
||||
FontSize="11" Margin="14,0,4,0"/>
|
||||
<ComboBox x:Name="RecentBox" ItemsSource="{Binding RecentFiles}"
|
||||
SelectionChanged="Recent_SelectionChanged"
|
||||
Width="200" Height="26" MinHeight="26" FontSize="11"
|
||||
VerticalContentAlignment="Center"
|
||||
ToolTip="최근에 견준 파일 짝. 고르면 바로 엽니다">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<!-- 자리를 옮겼거나 지워진 짝은 흐리게. 목록에서 지우지는 않는다 -
|
||||
네트워크 드라이브나 USB 는 잠깐 안 보일 뿐이다. -->
|
||||
<TextBlock Text="{Binding Label}" ToolTip="{Binding Tip}" FontSize="11">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Exists}" Value="False">
|
||||
<Setter Property="Opacity" Value="0.45"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Git 일 때: 비교할 파일과 3-Way 상대 브랜치를 여기서 고른다.
|
||||
|
||||
@ -915,6 +915,20 @@ namespace MergeHub.Views
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 최근 목록에서 하나를 고르면 연다.
|
||||
///
|
||||
/// 고른 뒤 바로 선택을 지운다 - 같은 짝을 다시 고를 수 있어야 하는데,
|
||||
/// 선택이 남아 있으면 두 번째부터는 아무 일도 일어나지 않는다.
|
||||
/// </summary>
|
||||
private void Recent_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (sender is not ComboBox box || box.SelectedItem is not Services.RecentPair pair) return;
|
||||
|
||||
box.SelectedIndex = -1;
|
||||
if (DataContext is HomeViewModel vm) vm.OpenRecent(pair);
|
||||
}
|
||||
|
||||
// ================= 끌어다 놓기 =================
|
||||
|
||||
/// <summary>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user