Skip to content

Commit b0a25c8

Browse files
authored
feat: add clip deletion from the UI (#152)
## Summary - Add `DeleteSelectedClip` command to remove the selected clip from the collection - Wire up Delete key binding, Edit > Delete Clip menu item, and right-click context menu on the clip list - Clean up orphaned files from disk when saving after deletions Closes #148
1 parent a28c222 commit b0a25c8

File tree

3 files changed

+84
-0
lines changed

3 files changed

+84
-0
lines changed

src/SharpFM/MainWindow.axaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<KeyBinding Gesture="Ctrl+N" Command="{Binding NewScriptCommand}" />
2222
<KeyBinding Gesture="Ctrl+Shift+N" Command="{Binding NewTableCommand}" />
2323
<KeyBinding Gesture="Ctrl+Shift+C" Command="{Binding CopySelectedToClip}" />
24+
<KeyBinding Gesture="Delete" Command="{Binding DeleteSelectedClip}" />
2425
</Window.KeyBindings>
2526

2627
<DockPanel>
@@ -39,6 +40,8 @@
3940
<MenuItem Command="{Binding CopySelectedToClip}" Header="Copy to FileMaker" InputGesture="Ctrl+Shift+C" />
4041
<Separator />
4142
<MenuItem Command="{Binding CopyAsClass}" Header="Copy as C# Class" />
43+
<Separator />
44+
<MenuItem Command="{Binding DeleteSelectedClip}" Header="Delete Clip" InputGesture="Delete" />
4245
</MenuItem>
4346
<MenuItem x:Name="pluginsMenu" Header="_Plugins">
4447
<!-- Plugin menu items are added dynamically in code-behind -->
@@ -138,6 +141,20 @@
138141
Classes="Fluent2"
139142
ItemsSource="{Binding FilteredClips}"
140143
SelectedItem="{Binding SelectedClip}">
144+
<ListBox.ContextMenu>
145+
<ContextMenu>
146+
<MenuItem
147+
Command="{Binding CopySelectedToClip}"
148+
Header="Copy to FileMaker" />
149+
<MenuItem
150+
Command="{Binding CopyAsClass}"
151+
Header="Copy as C# Class" />
152+
<Separator />
153+
<MenuItem
154+
Command="{Binding DeleteSelectedClip}"
155+
Header="Delete Clip" />
156+
</ContextMenu>
157+
</ListBox.ContextMenu>
141158
<ListBox.ItemTemplate>
142159
<DataTemplate>
143160
<Border

src/SharpFM/ViewModels/MainWindowViewModel.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,20 @@ public void SaveClipsStorage()
132132
}
133133

134134
clipContext.SaveChanges();
135+
136+
// Remove files for clips that no longer exist in memory
137+
var activeNames = new HashSet<string>(
138+
FileMakerClips.Select(c => $"{c.Name}.{c.ClipType}"),
139+
StringComparer.OrdinalIgnoreCase);
140+
141+
foreach (var file in Directory.EnumerateFiles(CurrentPath))
142+
{
143+
if (!activeNames.Contains(Path.GetFileName(file)))
144+
{
145+
File.Delete(file);
146+
}
147+
}
148+
135149
ShowStatus($"Saved {FileMakerClips.Count} clip(s) to {CurrentPath}");
136150
}
137151
catch (Exception e)
@@ -156,6 +170,20 @@ public void ExitApplication()
156170
private static readonly string EmptyTableXml =
157171
"<fmxmlsnippet type=\"FMObjectList\"><BaseTable name=\"NewTable\"></BaseTable></fmxmlsnippet>";
158172

173+
public void DeleteSelectedClip()
174+
{
175+
if (SelectedClip is null)
176+
{
177+
ShowStatus("No clip selected");
178+
return;
179+
}
180+
181+
var name = SelectedClip.Name;
182+
FileMakerClips.Remove(SelectedClip);
183+
SelectedClip = null;
184+
ShowStatus($"Deleted clip '{name}'");
185+
}
186+
159187
public void NewScriptCommand()
160188
{
161189
try

tests/SharpFM.Tests/ViewModels/MainWindowViewModelTests.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,45 @@ public void StatusMessage_NotifiesPropertyChanged()
122122
Assert.Equal("StatusMessage", changedProperty);
123123
}
124124

125+
[Fact]
126+
public void DeleteSelectedClip_RemovesClipFromCollection()
127+
{
128+
var vm = CreateVm();
129+
vm.NewScriptCommand();
130+
var clip = vm.SelectedClip;
131+
Assert.NotNull(clip);
132+
133+
vm.DeleteSelectedClip();
134+
135+
Assert.DoesNotContain(clip, vm.FileMakerClips);
136+
Assert.Null(vm.SelectedClip);
137+
Assert.Contains("Deleted clip", vm.StatusMessage);
138+
}
139+
140+
[Fact]
141+
public void DeleteSelectedClip_NoSelection_ShowsStatus()
142+
{
143+
var vm = CreateVm();
144+
vm.SelectedClip = null;
145+
146+
vm.DeleteSelectedClip();
147+
148+
Assert.Equal("No clip selected", vm.StatusMessage);
149+
}
150+
151+
[Fact]
152+
public void DeleteSelectedClip_RemovesFromFilteredClips()
153+
{
154+
var vm = CreateVm();
155+
vm.NewScriptCommand();
156+
var clip = vm.SelectedClip!;
157+
Assert.Contains(clip, vm.FilteredClips);
158+
159+
vm.DeleteSelectedClip();
160+
161+
Assert.DoesNotContain(clip, vm.FilteredClips);
162+
}
163+
125164
[Fact]
126165
public void SearchText_FiltersClips()
127166
{

0 commit comments

Comments
 (0)