From c1bc1c942aee854f931a53ff69c795a085aa8fd0 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Wed, 31 Jan 2024 10:50:02 +0700 Subject: [PATCH] group clans by url --- .../clan_vm_manager/models/use_vms.py | 38 ++++++++++++++++++ .../clan-vm-manager/clan_vm_manager/style.css | 12 ++++++ .../clan_vm_manager/views/list.py | 40 ++++++++++++++----- 3 files changed, 79 insertions(+), 11 deletions(-) diff --git a/pkgs/clan-vm-manager/clan_vm_manager/models/use_vms.py b/pkgs/clan-vm-manager/clan_vm_manager/models/use_vms.py index ab19c829d..4be7aef61 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/models/use_vms.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/models/use_vms.py @@ -26,6 +26,44 @@ from gi.repository import Gio, GLib, GObject log = logging.getLogger(__name__) +class ClanGroup(GObject.Object): + def __init__(self, url: str | Path, vms: list["VM"]) -> None: + super().__init__() + self.url = url + self.vms = vms + self.list_store = Gio.ListStore.new(VM) + + for vm in vms: + self.list_store.append(vm) + + +class Clans: + list_store: Gio.ListStore + _instance: "None | ClanGroup" = None + + # Make sure the VMS class is used as a singleton + def __init__(self) -> None: + raise RuntimeError("Call use() instead") + + @classmethod + def use(cls: Any) -> "ClanGroup": + if cls._instance is None: + cls._instance = cls.__new__(cls) + cls.list_store = Gio.ListStore.new(ClanGroup) + + groups: dict[str | Path, list["VM"]] = {} + for vm in get_saved_vms(): + ll = groups.get(vm.data.flake.flake_url, []) + ll.append(vm) + groups[vm.data.flake.flake_url] = ll + + for url, vms in groups.items(): + grp = ClanGroup(url, vms) + cls.list_store.append(grp) + + return cls._instance + + class VM(GObject.Object): # Define a custom signal with the name "vm_stopped" and a string argument for the message __gsignals__: ClassVar = { diff --git a/pkgs/clan-vm-manager/clan_vm_manager/style.css b/pkgs/clan-vm-manager/clan_vm_manager/style.css index 0539043b1..e64c6b27c 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/style.css +++ b/pkgs/clan-vm-manager/clan_vm_manager/style.css @@ -16,6 +16,18 @@ avatar { padding-bottom: 25px; } +.group-list { + background-color: inherit; +} +.group-list > .activatable:hover { + background-color: unset; +} +.group-list > row { + margin-top: 12px; + border-bottom: unset; +} + + .vm-list { margin-top: 25px; margin-bottom: 25px; diff --git a/pkgs/clan-vm-manager/clan_vm_manager/views/list.py b/pkgs/clan-vm-manager/clan_vm_manager/views/list.py index 08e1f5c7a..a14f9953d 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/views/list.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/views/list.py @@ -10,7 +10,7 @@ from clan_vm_manager.models.use_views import Views gi.require_version("Adw", "1") from gi.repository import Adw, Gdk, Gio, GObject, Gtk -from clan_vm_manager.models.use_vms import VM, VMS +from clan_vm_manager.models.use_vms import VM, VMS, ClanGroup, Clans def create_boxed_list( @@ -42,17 +42,17 @@ class ClanList(Gtk.Box): def __init__(self) -> None: super().__init__(orientation=Gtk.Orientation.VERTICAL) - vms = VMS.use() + groups = Clans.use() join = Join.use() self.join_boxed_list = create_boxed_list( model=join.list_store, render_row=self.render_join_row ) - self.vm_boxed_list = create_boxed_list( - model=vms.list_store, render_row=self.render_vm_row + self.group_list = create_boxed_list( + model=groups.list_store, render_row=self.render_group_row ) - self.vm_boxed_list.add_css_class("vm-list") + self.group_list.add_css_class("group-list") search_bar = Gtk.SearchBar() # This widget will typically be the top-level window @@ -65,13 +65,28 @@ class ClanList(Gtk.Box): self.append(search_bar) self.append(self.join_boxed_list) - self.append(self.vm_boxed_list) + self.append(self.group_list) + + def render_group_row(self, boxed_list: Gtk.ListBox, group: ClanGroup) -> Gtk.Widget: + # if boxed_list.has_css_class("no-shadow"): + # boxed_list.remove_css_class("no-shadow") + + grp = Adw.PreferencesGroup() + grp.set_title(group.url) + + vm_list = create_boxed_list( + model=group.list_store, render_row=self.render_vm_row + ) + + grp.add(vm_list) + + return grp def on_search_changed(self, entry: Gtk.SearchEntry) -> None: VMS.use().filter_by_name(entry.get_text()) # Disable the shadow if the list is empty if not VMS.use().list_store.get_n_items(): - self.vm_boxed_list.add_css_class("no-shadow") + self.group_list.add_css_class("no-shadow") def render_vm_row(self, boxed_list: Gtk.ListBox, vm: VM) -> Gtk.Widget: if boxed_list.has_css_class("no-shadow"): @@ -80,19 +95,22 @@ class ClanList(Gtk.Box): row = Adw.ActionRow() # Title - row.set_title(flake.clan_name) + row.set_title(flake.flake_attr) row.set_title_lines(1) row.set_title_selectable(True) # Subtitle - row.set_subtitle(vm.get_id()) + row.set_subtitle(flake.clan_name) row.set_subtitle_lines(1) # # Avatar avatar = Adw.Avatar() - avatar.set_custom_image(Gdk.Texture.new_from_filename(flake.icon)) - avatar.set_text(flake.clan_name + " " + flake.flake_attr) + if flake.icon: + avatar.set_custom_image(Gdk.Texture.new_from_filename(flake.icon)) + if not flake.icon: + avatar.set_text(flake.clan_name + " " + flake.flake_attr) + avatar.set_show_initials(True) avatar.set_size(50) row.add_prefix(avatar)