using System.Linq; using System.Windows; using System.Windows.Controls; using MergeHub.ViewModels; namespace MergeHub.Views { /// /// Git 탭. DataContext(GitViewModel)는 플랫폼 TabManager 가 DI 로 만들어 붙여준다. /// public partial class GitView : UserControl { public GitView() { InitializeComponent(); // 다른 탭에 있는 동안 파일 비교 탭에서 고른 커밋의 내용을, 이 탭이 다시 보일 때 그린다(10차 B-1). IsVisibleChanged += (_, e) => { if (e.NewValue is true) ViewModel?.ShowPendingDiff(); }; // 이력에서 다른 커밋을 누르면 파일 비교 탭도 따라간다. 거기 저장 안 한 편집이 있으면 선택이 바뀌기 전에 묻고, // [취소] 면 누름을 막아 선택이 그대로다(10차 B-1). HistoryList.PreviewMouseLeftButtonDown += (_, e) => { if (ViewModel is not { } vm) return; if (ItemsControl.ContainerFromElement(HistoryList, e.OriginalSource as DependencyObject) is not ListBoxItem item) return; if (item.DataContext is not MergeHub.Models.CommitInfo target) return; var answer = vm.ConfirmChooseCommit(target); if (answer is null) return; // 물을 것 없음 - 목록이 여느 때처럼 고른다 e.Handled = true; // [취소] 거나, 창이 누름을 끊어 목록이 스스로 못 고른다 if (answer == true) ChooseHistoryRow(vm, HistoryList.Items.IndexOf(target)); }; // 우클릭한 줄을 먼저 고른다(11차 C-2). 목록은 오른쪽 누름으로는 선택을 바꾸지 않아서, 그대로 두면 // 가리킨 줄이 아닌 다른 커밋의 메시지를 바꾸게 된다 - 다시 쓴 이력은 되돌리기 번거롭다. HistoryList.PreviewMouseRightButtonDown += (_, e) => { if (ViewModel is not { } vm) return; if (ItemsControl.ContainerFromElement(HistoryList, e.OriginalSource as DependencyObject) is not ListBoxItem item) return; if (item.DataContext is not MergeHub.Models.CommitInfo target) return; var answer = vm.ConfirmChooseCommit(target); if (answer == false) { e.Handled = true; return; } // [취소] - 메뉴도 열지 않는다 ChooseHistoryRow(vm, HistoryList.Items.IndexOf(target)); }; HistoryList.PreviewKeyDown += (_, e) => { if (ViewModel is not { } vm) return; var at = HistoryList.SelectedIndex; var to = e.Key switch { System.Windows.Input.Key.Up => at - 1, System.Windows.Input.Key.Down => at + 1, System.Windows.Input.Key.Home => 0, System.Windows.Input.Key.End => HistoryList.Items.Count - 1, System.Windows.Input.Key.PageUp => at - 10, System.Windows.Input.Key.PageDown => at + 10, _ => at }; to = System.Math.Max(0, System.Math.Min(HistoryList.Items.Count - 1, to)); if (to == at) return; var answer = vm.ConfirmChooseCommit(HistoryList.Items[to] as MergeHub.Models.CommitInfo); if (answer is null) return; e.Handled = true; if (answer == true) ChooseHistoryRow(vm, to); }; } /// 물은 뒤 [계속] 이면 그 커밋을 직접 고른다. 창이 누름을 끊어 목록이 스스로 고르지 못한다(10차 B-1). private void ChooseHistoryRow(GitViewModel vm, int index) { if (index < 0 || index >= HistoryList.Items.Count) return; vm.SelectedCommit = HistoryList.Items[index] as MergeHub.Models.CommitInfo; HistoryList.ScrollIntoView(HistoryList.Items[index]); (HistoryList.ItemContainerGenerator.ContainerFromIndex(index) as ListBoxItem)?.Focus(); } private GitViewModel? ViewModel => DataContext as GitViewModel; private void BrowseFolder_Click(object sender, RoutedEventArgs e) { var vm = ViewModel; if (vm is null) return; // 예전에는 WinForms FolderBrowserDialog 를 썼다. net48 에서는 옛날 트리 창이라 // 주소 칸이 없고 경로를 붙여넣을 수도 없어서, 깊은 폴더가 기억돼 있으면 // 저장소 최상위까지 거슬러 올라가기가 번거로웠다. FolderPicker.cs 참고. var picked = MergeHub.Services.FolderPicker.Pick("Git 저장소 폴더 고르기", vm.Workspace.RepositoryPath); if (picked is null) return; // .git 은 최상위에만 있다. 하위 폴더를 골랐어도 위로 거슬러 올라가 최상위를 연다 - // 사람은 지금 보고 있던 깊은 폴더를 고르기 마련이다. var root = MergeHub.Services.PathUtil.FindRepositoryRoot(picked); vm.Workspace.RepositoryPath = root.Length > 0 ? root : picked; if (vm.OpenCommand.CanExecute(null)) vm.OpenCommand.Execute(null); } // PasswordBox 는 보안상 Password 를 바인딩할 수 없어 코드 비하인드로 전달한다. /// 충돌 목록을 두 번 누르면 파일 비교 탭의 3분할에서 연다(6차 B-1). 항목 위가 아니면 무시한다. private void Conflicts_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e) { if (e.OriginalSource is not System.Windows.DependencyObject hit) return; if (System.Windows.Controls.ItemsControl.ContainerFromElement((System.Windows.Controls.ItemsControl)sender, hit) is null) return; if (DataContext is MergeHub.ViewModels.GitViewModel vm && vm.OpenConflictInCompareCommand.CanExecute(null)) vm.OpenConflictInCompareCommand.Execute(null); } /// 고른 충돌 파일들을 넘긴다(6차 B-6). ListBox.SelectedItems 는 바인딩할 수 없다. private void Conflicts_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (sender is not ListBox list || ViewModel is not { } vm) return; vm.SetSelectedConflicts(list.SelectedItems.OfType().ToList()); } private void Password_Changed(object sender, RoutedEventArgs e) { if (sender is PasswordBox box && ViewModel is { } vm) vm.Workspace.Password = box.Password; } private void Log_TextChanged(object sender, TextChangedEventArgs e) { if (sender is TextBox box) box.ScrollToEnd(); } } }