diff --git a/src/WebCamControl.Core/IPresets.cs b/src/WebCamControl.Core/IPresets.cs index e64b0ba..d85a838 100644 --- a/src/WebCamControl.Core/IPresets.cs +++ b/src/WebCamControl.Core/IPresets.cs @@ -30,5 +30,10 @@ public interface IPresets /// /// Preset index to overwrite. If null, a new preset will be saved. public void SaveCurrent(ICamera camera, string name, int? index); + + /// + /// Deletes the preset at the specified index. + /// + public void Delete(PresetConfig preset); } diff --git a/src/WebCamControl.Core/Presets.cs b/src/WebCamControl.Core/Presets.cs index c2a11e0..8259862 100644 --- a/src/WebCamControl.Core/Presets.cs +++ b/src/WebCamControl.Core/Presets.cs @@ -67,4 +67,14 @@ public void SaveCurrent(ICamera camera, string name, int? index) _configManager.Save(); OnChange?.Invoke(this, EventArgs.Empty); } + + /// + /// Deletes the preset at the specified index. + /// + public void Delete(PresetConfig preset) + { + PresetConfigs.Remove(preset); + _configManager.Save(); + OnChange?.Invoke(this, EventArgs.Empty); + } } diff --git a/src/WebCamControl.Gtk/FullWindow.blp b/src/WebCamControl.Gtk/FullWindow.blp index 4b5dea3..0382dd0 100644 --- a/src/WebCamControl.Gtk/FullWindow.blp +++ b/src/WebCamControl.Gtk/FullWindow.blp @@ -43,6 +43,16 @@ Adw.ApplicationWindow full_window { } }; } + Adw.ViewStackPage { + name: "presets"; + title: _("Presets"); + child: ListBox _presetsList { + styles [ + "boxed-list", + "settings-page", + ] + }; + } } }; } diff --git a/src/WebCamControl.Gtk/FullWindow.cs b/src/WebCamControl.Gtk/FullWindow.cs index cfe7e52..3c2b30f 100644 --- a/src/WebCamControl.Gtk/FullWindow.cs +++ b/src/WebCamControl.Gtk/FullWindow.cs @@ -1,8 +1,6 @@ using Adw; using Gtk; using WebCamControl.Core; -using WebCamControl.Core.Linux; -using WebCamControl.Gtk.Extensions; using WebCamControl.Gtk.Widgets; namespace WebCamControl.Gtk; @@ -13,26 +11,31 @@ namespace WebCamControl.Gtk; public class FullWindow : Adw.Window { private readonly ICamera _camera; + private readonly IPresets _presets; #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value [Connect] private readonly ListBox _controls = default!; [Connect] private readonly ActionRow _exampleRow = default!; + [Connect] private readonly ListBox _presetsList = default!; #pragma warning restore CS0649 // Field is never assigned to, and will always have its default value public FullWindow( Adw.Application app, - ICamera camera - ) : this(new Builder("FullWindow.ui"), camera) + ICamera camera, + IPresets presets + ) : this(new Builder("FullWindow.ui"), camera, presets) { Application = app; } private FullWindow( Builder builder, - ICamera camera + ICamera camera, + IPresets presets ) : base(builder.GetPointer("full_window"), false) { _camera = camera; + _presets = presets; builder.Connect(this); // TODO: Configure proper icon @@ -57,5 +60,19 @@ private void InitializeWidgets() { _controls.Append(control!); } + + InitializePresets(); + _presets.OnChange += (_, _) => InitializePresets(); + } + + private void InitializePresets() + { + _presetsList.RemoveAll(); + foreach (var preset in _presets.PresetConfigs) + { + var row = new PresetRow(preset); + row.OnDelete += (_, _) => _presets.Delete(preset); + _presetsList.Append(row); + } } } diff --git a/src/WebCamControl.Gtk/Widgets/PresetRow.cs b/src/WebCamControl.Gtk/Widgets/PresetRow.cs new file mode 100644 index 0000000..e00ad1d --- /dev/null +++ b/src/WebCamControl.Gtk/Widgets/PresetRow.cs @@ -0,0 +1,70 @@ +using Adw; +using Gtk; +using WebCamControl.Core.Configuration; +using MessageDialog = Adw.MessageDialog; + +namespace WebCamControl.Gtk.Widgets; + +/// +/// A row representing a saved preset. +/// +public class PresetRow : ExpanderRow +{ + private readonly PresetConfig _preset; + public event EventHandler? OnDelete; + + public PresetRow(PresetConfig preset) + : this(Adw.Internal.ExpanderRow.New(), false, preset) + { + + } + private PresetRow(IntPtr ptr, bool ownedRef, PresetConfig preset) + : base(ptr, ownedRef) + { + _preset = preset; + Title = preset.Name; + + if (preset.Tilt != null) + { + var tilt = ActionRow.New(); + tilt.Title = $"Tilt: {Math.Round((decimal)preset.Tilt.Value, decimals: 2)}"; + AddRow(tilt); + } + if (preset.Pan != null) + { + var pan = ActionRow.New(); + pan.Title = $"Pan: {Math.Round((decimal)preset.Pan.Value, decimals: 2)}"; + AddRow(pan); + } + + var deleteButton = Button.New(); + deleteButton.TooltipText = $"Delete {preset.Name}"; + deleteButton.IconName = "user-trash-symbolic"; + deleteButton.CssClasses = ["flat", "image-button"]; + deleteButton.OnClicked += (_, _) => ConfirmDelete(); + AddSuffix(deleteButton); + } + + private void ConfirmDelete() + { + const string deleteButtonId = "delete"; + const string cancelButtonId = "cancel"; + + var dialog = new MessageDialog(); + dialog.SetParent(this); + dialog.Body = $"Are you sure you want to delete preset '{_preset.Name}'?"; + dialog.AddResponse(cancelButtonId, "Cancel"); + dialog.AddResponse(deleteButtonId, "Delete"); + dialog.SetResponseAppearance(deleteButtonId, ResponseAppearance.Destructive); + dialog.SetDefaultResponse(cancelButtonId); + dialog.SetCloseResponse(cancelButtonId); + dialog.OnResponse += (_, args) => + { + if (args.Response == deleteButtonId) + { + OnDelete?.Invoke(this, EventArgs.Empty); + } + }; + dialog.Present(); + } +}