Skip to content

Commit 29b9db9

Browse files
committed
refactor: context menu for selected changes (#2661)
Signed-off-by: leo <longshuang@msn.cn>
1 parent a4f3fa7 commit 29b9db9

13 files changed

Lines changed: 339 additions & 249 deletions

src/ViewModels/ChangeSelection.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public class ChangeSelection
88
public List<Models.Change> Changes { get; }
99
public bool IsSingleFolder { get; }
1010
public string SingleFolderPath { get; }
11+
public bool HasFolder { get; }
1112

1213
public int Count => Changes.Count;
1314

@@ -17,15 +18,23 @@ public ChangeSelection(IList selected)
1718

1819
IsSingleFolder = false;
1920
SingleFolderPath = null;
21+
HasFolder = false;
2022

2123
if (selected is { Count: > 0 })
2224
{
2325
foreach (var item in selected)
2426
{
2527
if (item is Models.Change c)
28+
{
2629
changes.Add(c);
30+
}
2731
else if (item is ChangeTreeNode node)
32+
{
2833
CollectChangesInNode(changes, node);
34+
35+
if (node.IsFolder && !HasFolder)
36+
HasFolder = true;
37+
}
2938
}
3039

3140
if (selected.Count == 1 && selected[0] is ChangeTreeNode { IsFolder: true } folder)
@@ -43,6 +52,7 @@ public bool IsChanged(ChangeSelection other)
4352
if (other == null ||
4453
IsSingleFolder != other.IsSingleFolder ||
4554
SingleFolderPath != other.SingleFolderPath ||
55+
HasFolder != other.HasFolder ||
4656
Changes.Count != other.Changes.Count)
4757
return true;
4858

src/ViewModels/CommitDetail.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ private void Refresh()
541541

542542
private void UpdateDetails()
543543
{
544-
if (ActiveTabIndex == 1 && _changeSelection is { Count: 1, IsSingleFolder: false })
544+
if (ActiveTabIndex == 1 && _changeSelection is { Count: 1, HasFolder: false })
545545
DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption(_commit, _changeSelection.Changes[0]), _diffContext);
546546
else
547547
DiffContext = null;

src/ViewModels/Compare.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public ChangeSelection ChangeSelection
7575
{
7676
if (SetProperty(ref _changeSelection, value))
7777
{
78-
if (value is { Count: 1, IsSingleFolder: false })
78+
if (value is { Count: 1, HasFolder: false })
7979
DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption(_based, _to, value.Changes[0]), _diffContext);
8080
else
8181
DiffContext = null;

src/ViewModels/RevisionCompare.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public ChangeSelection ChangeSelection
7272
{
7373
if (SetProperty(ref _changeSelection, value))
7474
{
75-
if (value is { Count: 1, IsSingleFolder: false })
75+
if (value is { Count: 1, HasFolder: false })
7676
DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption(GetSHA(_startPoint), GetSHA(_endPoint), value.Changes[0]), _diffContext);
7777
else
7878
DiffContext = null;

src/ViewModels/StashesPage.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public ChangeSelection ChangeSelection
104104
{
105105
if (SetProperty(ref _changeSelection, value))
106106
{
107-
if (value is not { Count: 1, IsSingleFolder: false })
107+
if (value is not { Count: 1, HasFolder: false })
108108
DiffContext = null;
109109
else if (_untracked.Contains(value.Changes[0]))
110110
DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption(_selectedStash.UntrackedParent, _selectedStash.Parents[2], value.Changes[0]), _diffContext);

src/ViewModels/SubmoduleRevisionCompare.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public ChangeSelection ChangeSelection
4545
{
4646
if (SetProperty(ref _changeSelection, value))
4747
{
48-
if (value is { Count: 1, IsSingleFolder: false })
48+
if (value is { Count: 1, HasFolder: false })
4949
DiffContext = new DiffContext(_repo, new Models.DiffOption(_base.SHA, _to.SHA, value.Changes[0]), _diffContext);
5050
else
5151
DiffContext = null;

src/ViewModels/WorkingCopy.cs

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -168,21 +168,16 @@ public ChangeSelection SelectedUnstaged
168168
{
169169
if (SetProperty(ref _selectedUnstaged, value))
170170
{
171-
if (value == null || value.Count == 0)
172-
{
173-
if (_selectedStaged == null || _selectedStaged.Count == 0)
174-
SetDetail(null, true);
175-
}
176-
else
171+
if (value is { Count: > 0 })
177172
{
173+
_isLoadingData = true;
178174
if (_selectedStaged is { Count: > 0 })
179175
SelectedStaged = new(null);
180-
181-
if (value.Count == 1 && !value.IsSingleFolder)
182-
SetDetail(value.Changes[0], true);
183-
else
184-
SetDetail(null, true);
176+
_isLoadingData = false;
185177
}
178+
179+
if (!_isLoadingData)
180+
UpdateDetail();
186181
}
187182
}
188183
}
@@ -194,21 +189,16 @@ public ChangeSelection SelectedStaged
194189
{
195190
if (SetProperty(ref _selectedStaged, value))
196191
{
197-
if (value == null || value.Count == 0)
198-
{
199-
if (_selectedUnstaged == null || _selectedUnstaged.Count == 0)
200-
SetDetail(null, false);
201-
}
202-
else
192+
if (value is { Count: > 0 })
203193
{
194+
_isLoadingData = true;
204195
if (_selectedUnstaged is { Count: > 0 })
205196
SelectedUnstaged = new(null);
206-
207-
if (value.Count == 1 && !value.IsSingleFolder)
208-
SetDetail(value.Changes[0], false);
209-
else
210-
SetDetail(null, false);
197+
_isLoadingData = false;
211198
}
199+
200+
if (!_isLoadingData)
201+
UpdateDetail();
212202
}
213203
}
214204
}
@@ -757,9 +747,9 @@ public async Task CommitAsync(bool autoStage, bool autoPush)
757747

758748
private void UpdateDetail()
759749
{
760-
if (_selectedUnstaged is { Count: 1, IsSingleFolder: false })
750+
if (_selectedUnstaged is { Count: 1, HasFolder: false })
761751
SetDetail(_selectedUnstaged.Changes[0], true);
762-
else if (_selectedStaged is { Count: 1, IsSingleFolder: false })
752+
else if (_selectedStaged is { Count: 1, HasFolder: false })
763753
SetDetail(_selectedStaged.Changes[0], false);
764754
else
765755
SetDetail(null, false);

src/Views/CommitChanges.axaml.cs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e
2727
CreateChangeContextMenuByFolder(selection.SingleFolderPath, selection.Changes)?.Open(view);
2828
else if (selection.Changes.Count > 1)
2929
CreateMultipleChangesContextMenu(selection.Changes)?.Open(view);
30+
else if (selection.HasFolder)
31+
CreateChangeContextMenuByMultipleFolders(selection.Changes[0])?.Open(view);
3032
else
3133
this.FindAncestorOfType<CommitDetail>()?.CreateChangeContextMenu(selection.Changes[0])?.Open(view);
3234
}
@@ -156,6 +158,71 @@ private ContextMenu CreateChangeContextMenuByFolder(string folder, List<Models.C
156158
return menu;
157159
}
158160

161+
private ContextMenu CreateChangeContextMenuByMultipleFolders(Models.Change change)
162+
{
163+
if (DataContext is not ViewModels.CommitDetail { Repository: { } repo, Commit: { } commit } vm)
164+
return null;
165+
166+
var patch = new MenuItem();
167+
patch.Header = App.Text("FileCM.SaveAsPatch");
168+
patch.Icon = this.CreateMenuIcon("Icons.Save");
169+
patch.Click += async (_, e) =>
170+
{
171+
var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider;
172+
if (storageProvider == null)
173+
return;
174+
175+
var options = new FilePickerSaveOptions();
176+
options.Title = App.Text("FileCM.SaveAsPatch");
177+
options.DefaultExtension = ".patch";
178+
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }];
179+
180+
try
181+
{
182+
var storageFile = await storageProvider.SaveFilePickerAsync(options);
183+
if (storageFile != null)
184+
{
185+
var saveTo = storageFile.Path.LocalPath;
186+
await vm.SaveChangesAsPatchAsync([change], saveTo);
187+
}
188+
}
189+
catch (Exception exception)
190+
{
191+
repo.SendNotification($"Failed to save as patch: {exception.Message}", true);
192+
}
193+
194+
e.Handled = true;
195+
};
196+
197+
var copyPath = new MenuItem();
198+
copyPath.Header = App.Text("CopyPath");
199+
copyPath.Icon = this.CreateMenuIcon("Icons.Copy");
200+
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
201+
copyPath.Click += async (_, ev) =>
202+
{
203+
await this.CopyTextAsync(change.Path);
204+
ev.Handled = true;
205+
};
206+
207+
var copyFullPath = new MenuItem();
208+
copyFullPath.Header = App.Text("CopyFullPath");
209+
copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy");
210+
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
211+
copyFullPath.Click += async (_, e) =>
212+
{
213+
await this.CopyTextAsync(vm.GetAbsPath(change.Path));
214+
e.Handled = true;
215+
};
216+
217+
var menu = new ContextMenu();
218+
menu.Items.Add(patch);
219+
menu.Items.Add(new MenuItem { Header = "-" });
220+
menu.Items.Add(copyPath);
221+
menu.Items.Add(copyFullPath);
222+
223+
return menu;
224+
}
225+
159226
private ContextMenu CreateMultipleChangesContextMenu(List<Models.Change> changes)
160227
{
161228
if (DataContext is not ViewModels.CommitDetail { Repository: { } repo, Commit: { } commit } vm)

src/Views/Compare.axaml.cs

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -53,40 +53,47 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e
5353
e.Handled = true;
5454
};
5555

56-
var selectedFolder = selection.IsSingleFolder;
57-
var fullPathOfFolder = selectedFolder ? vm.GetAbsPath(selection.SingleFolderPath) : null;
58-
var relativePathOfFolder = selectedFolder ? selection.SingleFolderPath : null;
56+
var selectedSingleFolder = selection.IsSingleFolder;
57+
var fullPathOfFolder = selectedSingleFolder ? vm.GetAbsPath(selection.SingleFolderPath) : null;
58+
var relativePathOfFolder = selectedSingleFolder ? selection.SingleFolderPath : null;
5959

6060
var menu = new ContextMenu();
6161
if (selection.Count == 1)
6262
{
6363
var change = selection.Changes[0];
6464
var changeFullPath = vm.GetAbsPath(change.Path);
6565

66-
var openWithMerger = new MenuItem();
67-
openWithMerger.Header = App.Text("OpenInExternalMergeTool");
68-
openWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith");
69-
openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D";
70-
openWithMerger.IsVisible = !selectedFolder;
71-
openWithMerger.Click += (_, ev) =>
66+
if (!selection.HasFolder)
7267
{
73-
vm.OpenInExternalDiffTool(change);
74-
ev.Handled = true;
75-
};
68+
var openWithMerger = new MenuItem();
69+
openWithMerger.Header = App.Text("OpenInExternalMergeTool");
70+
openWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith");
71+
openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D";
72+
openWithMerger.Click += (_, ev) =>
73+
{
74+
vm.OpenInExternalDiffTool(change);
75+
ev.Handled = true;
76+
};
77+
menu.Items.Add(openWithMerger);
78+
}
7679

77-
var explore = new MenuItem();
78-
explore.Header = App.Text("RevealFile");
79-
explore.Icon = this.CreateMenuIcon("Icons.Explore");
80-
explore.IsEnabled = selectedFolder ? Directory.Exists(fullPathOfFolder) : File.Exists(changeFullPath);
81-
explore.Click += (_, ev) =>
80+
if (!selection.HasFolder || selectedSingleFolder)
8281
{
83-
Native.OS.OpenInFileManager(selectedFolder ? fullPathOfFolder : changeFullPath);
84-
ev.Handled = true;
85-
};
82+
var explore = new MenuItem();
83+
explore.Header = App.Text("RevealFile");
84+
explore.Icon = this.CreateMenuIcon("Icons.Explore");
85+
explore.IsEnabled = selectedSingleFolder ? Directory.Exists(fullPathOfFolder) : File.Exists(changeFullPath);
86+
explore.Click += (_, ev) =>
87+
{
88+
Native.OS.OpenInFileManager(selectedSingleFolder ? fullPathOfFolder : changeFullPath);
89+
ev.Handled = true;
90+
};
91+
menu.Items.Add(explore);
92+
}
93+
94+
if (menu.Items.Count > 0)
95+
menu.Items.Add(new MenuItem() { Header = "-" });
8696

87-
menu.Items.Add(openWithMerger);
88-
menu.Items.Add(explore);
89-
menu.Items.Add(new MenuItem() { Header = "-" });
9097
menu.Items.Add(patch);
9198

9299
if (vm.CanResetFiles)
@@ -120,7 +127,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e
120127
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
121128
copyPath.Click += async (_, ev) =>
122129
{
123-
await this.CopyTextAsync(selectedFolder ? relativePathOfFolder : change.Path);
130+
await this.CopyTextAsync(selectedSingleFolder ? relativePathOfFolder : change.Path);
124131
ev.Handled = true;
125132
};
126133

@@ -130,7 +137,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e
130137
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
131138
copyFullPath.Click += async (_, ev) =>
132139
{
133-
await this.CopyTextAsync(selectedFolder ? fullPathOfFolder : changeFullPath);
140+
await this.CopyTextAsync(selectedSingleFolder ? fullPathOfFolder : changeFullPath);
134141
ev.Handled = true;
135142
};
136143

@@ -140,7 +147,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e
140147
}
141148
else
142149
{
143-
if (selectedFolder)
150+
if (selectedSingleFolder)
144151
{
145152
var explore = new MenuItem();
146153
explore.Header = App.Text("RevealFile");
@@ -189,7 +196,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e
189196
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
190197
copyPath.Click += async (_, ev) =>
191198
{
192-
if (selectedFolder)
199+
if (selectedSingleFolder)
193200
{
194201
await this.CopyTextAsync(relativePathOfFolder);
195202
}
@@ -211,7 +218,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e
211218
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
212219
copyFullPath.Click += async (_, ev) =>
213220
{
214-
if (selectedFolder)
221+
if (selectedSingleFolder)
215222
{
216223
await this.CopyTextAsync(fullPathOfFolder);
217224
}

0 commit comments

Comments
 (0)