From b0379fba0538d697d8286817476daee299415f86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miskolczi=20Rich=C3=A1rd?= Date: Wed, 17 Sep 2025 12:49:05 +0200 Subject: [PATCH] Sidebar shortcuts bound --- App/UI/MainWindow.cs | 80 +++++++-- App/UI/MainWindow.xml | 154 ++++++++++-------- App/UI/Models/PasswordStoreShortcut.cs | 50 ++++++ .../Models/PasswordStoreShortcutCollection.cs | 86 ++++++++++ 4 files changed, 287 insertions(+), 83 deletions(-) create mode 100644 App/UI/Models/PasswordStoreShortcut.cs create mode 100644 App/UI/Models/PasswordStoreShortcutCollection.cs diff --git a/App/UI/MainWindow.cs b/App/UI/MainWindow.cs index ef26f87..412a381 100644 --- a/App/UI/MainWindow.cs +++ b/App/UI/MainWindow.cs @@ -1,21 +1,69 @@ -namespace App.UI; +using Adw; +using App.UI.Models; -public class MainWindow -{ - public Adw.Window Window { get; } +namespace App.UI; + +public class MainWindow +{ + public Window Window { get; } + private PreferencesGroup shortcutsGroup; + private PasswordStoreShortcutCollection shortcuts; + + public MainWindow() + { + var builder = new Gtk.Builder("App.UI.MainWindow.xml"); - public MainWindow() - { - var assembly = typeof(MainWindow).Assembly; - using var stream = assembly.GetManifestResourceStream("App.UI.MainWindow.xml"); - if (stream == null) - throw new Exception("Failed to load embedded resource MainWindow.xml"); - using var reader = new System.IO.StreamReader(stream); - var xml = reader.ReadToEnd(); - var builder = Gtk.Builder.NewFromString(xml, -1); - var window = builder.GetObject("main_window") as Adw.Window; + var window = builder.GetObject("main_window") as Window; if (window == null) - throw new Exception("Failed to load main_window from MainWindow.ui"); + { + throw new Exception("Failed to load embedded resource MainWindow.xml"); + } Window = window; - } + + var group = builder.GetObject("shortcuts_group") as PreferencesGroup; + if (group == null) + { + throw new Exception("Failed to load UI element with ID: shortcuts_group"); + } + shortcutsGroup = group; + + var addButton = builder.GetObject("add_shortcut_button") as ActionRow; + if (addButton == null) + { + throw new Exception("Failed to load UI element with ID: add_shortcut_button"); + } + addButton.OnActivated += OnAddShortcutClicked; + + // Initialize the observable collection with property binding + shortcuts = new PasswordStoreShortcutCollection(shortcutsGroup); + + LoadDefaultShortcuts(); + } + + private void OnAddShortcutClicked(object sender, EventArgs e) + { + AddShortcut("/path/to/location"); + } + + public void AddShortcut(string path) + { + var newShortcut = new PasswordStoreShortcut(path:path); + shortcuts.Add(newShortcut); // This will automatically update the UI + } + + public void RemoveShortcut(PasswordStoreShortcut shortcut) + { + shortcuts.Remove(shortcut); // This will automatically update the UI + } + + private void LoadDefaultShortcuts() + { + shortcuts.Add(new PasswordStoreShortcut(displayName:"Default", path:Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)+"/.password_store")); + } + + // Example of reactive property updates + public void UpdateShortcutName(PasswordStoreShortcut shortcut, string newName) + { + shortcut.DisplayName = newName; // This will automatically update the UI row + } } \ No newline at end of file diff --git a/App/UI/MainWindow.xml b/App/UI/MainWindow.xml index a94d300..849fb0d 100644 --- a/App/UI/MainWindow.xml +++ b/App/UI/MainWindow.xml @@ -1,68 +1,88 @@ - - - - 280 - 200 - 800 - 500 - - - max-width: 500sp - True - True - - - - - 200 - 200 - - - - Sidebar - - - - - - - - I am a sidebar - - - - - - - - - Content - - - - - - - sidebar-show-symbolic - True - False - - - - - - - Hello world - - - - - - - - - + + + + 280 + 200 + 800 + 500 + + + max-width: 500sp + True + True + + + + + 250 + 250 + + + + Password stores + + + + + + + + + + + + + + + + + + Add + True + + + list-add-symbolic + + + + + + + + + + + + + + + Content + + + + + + + sidebar-show-symbolic + True + False + + + + + + + Hello world + + + + + + + + + \ No newline at end of file diff --git a/App/UI/Models/PasswordStoreShortcut.cs b/App/UI/Models/PasswordStoreShortcut.cs new file mode 100644 index 0000000..19b6fb6 --- /dev/null +++ b/App/UI/Models/PasswordStoreShortcut.cs @@ -0,0 +1,50 @@ +using System.ComponentModel; + +namespace App.UI.Models; + +public class PasswordStoreShortcut : INotifyPropertyChanged +{ + public event PropertyChangedEventHandler? PropertyChanged; + + private string displayName; + private bool displayNameSet = false; + private string? iconName; + private string path; + public bool DisplayNameSet { get => displayNameSet; } + + public string DisplayName + { + get => displayName; + set + { + displayName = value; + displayNameSet = true; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DisplayName))); + } + } + + public string? IconName + { + get => iconName; + set { iconName = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IconName))); } + } + + public string Path + { + get => path; + } + + public PasswordStoreShortcut(string path, string iconName = "text-x-generic-symbolic", string? displayName = null) + { + this.path = path; + + this.iconName = iconName; + + this.displayName = path; + + if (displayName != null) + { + DisplayName = displayName; + } + } +} diff --git a/App/UI/Models/PasswordStoreShortcutCollection.cs b/App/UI/Models/PasswordStoreShortcutCollection.cs new file mode 100644 index 0000000..4233198 --- /dev/null +++ b/App/UI/Models/PasswordStoreShortcutCollection.cs @@ -0,0 +1,86 @@ +namespace App.UI.Models; + +using Adw; +using System.Collections.ObjectModel; +using System.Collections.Specialized; + +public class PasswordStoreShortcutCollection : ObservableCollection +{ + private readonly PreferencesGroup shortcutsGroup; + private readonly Dictionary itemToRowMap = new(); + + public PasswordStoreShortcutCollection(PreferencesGroup shortcutsGroup) + { + this.shortcutsGroup = shortcutsGroup; + CollectionChanged += OnCollectionChanged; + } + + private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + foreach (PasswordStoreShortcut item in e.NewItems) + { + var row = CreateShortcutRow(item); + itemToRowMap[item] = row; + shortcutsGroup.Add(row); + + // Subscribe to property changes for reactive updates + item.PropertyChanged += (sender, args) => UpdateRowFromItem(item, ref row); + } + break; + + case NotifyCollectionChangedAction.Remove: + foreach (PasswordStoreShortcut item in e.OldItems) + { + if (itemToRowMap.TryGetValue(item, out var row)) + { + shortcutsGroup.Remove(row); + itemToRowMap.Remove(item); + } + } + break; + + case NotifyCollectionChangedAction.Reset: + foreach (var row in itemToRowMap.Values) + { + shortcutsGroup.Remove(row); + } + itemToRowMap.Clear(); + break; + } + } + + private ActionRow CreateShortcutRow(PasswordStoreShortcut shortcut) + { + var row = new ActionRow(); + UpdateRowFromItem(shortcut, ref row); + + row.SetActivatable(true); + row.OnActivated += (sender, args) => { + Console.WriteLine($"[DEBUG] Opening: {shortcut.Path}"); + }; + + return row; + } + + private void UpdateRowFromItem(PasswordStoreShortcut shortcut, ref ActionRow row) + { + row.SetTitle(shortcut.DisplayName); + row.SetSubtitle(shortcut.Path); + + //Update icon + var existingIcon = row.GetFirstChild() as Gtk.Image; + if (existingIcon == null) + { + var icon = new Gtk.Image(); + icon.SetFromIconName(shortcut.IconName); + row.AddPrefix(icon); + } + else + { + existingIcon.SetFromIconName(shortcut.IconName); + } + } +} \ No newline at end of file