From af6722daa05dc92609353253d4b29ad216cc1b4c Mon Sep 17 00:00:00 2001 From: Qubasa Date: Tue, 2 Jan 2024 07:24:30 +0100 Subject: [PATCH] vm-manager: Added right click context menu --- pkgs/clan-cli/clan_cli/history/add.py | 6 ++- .../clan-vm-manager/clan_vm_manager/models.py | 4 +- .../clan_vm_manager/ui/clan_select_list.py | 24 +++++++++++- .../clan_vm_manager/ui/context_menu.py | 39 +++++++++++++++++++ 4 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 pkgs/clan-vm-manager/clan_vm_manager/ui/context_menu.py diff --git a/pkgs/clan-cli/clan_cli/history/add.py b/pkgs/clan-cli/clan_cli/history/add.py index 0682208b9..c753b6f3b 100644 --- a/pkgs/clan-cli/clan_cli/history/add.py +++ b/pkgs/clan-cli/clan_cli/history/add.py @@ -9,8 +9,9 @@ from clan_cli.flakes.inspect import FlakeConfig, inspect_flake from ..clan_uri import ClanURI from ..dirs import user_history_file -from ..locked_open import read_history_file, write_history_file from ..errors import ClanError +from ..locked_open import read_history_file, write_history_file + class EnhancedJSONEncoder(json.JSONEncoder): def default(self, o: Any) -> Any: @@ -53,7 +54,8 @@ def list_history() -> list[HistoryEntry]: try: parsed = read_history_file() for i, p in enumerate(parsed.copy()): - parsed[i] = merge_dicts(p, p["settings"]) + # Everything from the settings dict is merged into the flake dict, and can override existing values + parsed[i] = merge_dicts(p, p.get("settings", {})) logs = [HistoryEntry(**p) for p in parsed] except (json.JSONDecodeError, TypeError) as ex: raise ClanError(f"History file at {user_history_file()} is corrupted") from ex diff --git a/pkgs/clan-vm-manager/clan_vm_manager/models.py b/pkgs/clan-vm-manager/clan_vm_manager/models.py index 87b026b5c..0e7b2b248 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/models.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/models.py @@ -10,6 +10,7 @@ from .errors.show_error import show_error_dialog gi.require_version("GdkPixbuf", "2.0") +from clan_cli.errors import ClanError from gi.repository import GdkPixbuf from clan_vm_manager import assets @@ -60,6 +61,7 @@ class VM: description: str | None = None +# TODO: How to handle incompatible / corrupted history file. Delete it? # start/end indexes can be used optionally for pagination def get_initial_vms( running_vms: list[str], start: int = 0, end: int | None = None @@ -85,7 +87,7 @@ def get_initial_vms( _flake_attr=entry.flake.flake_attr, ) vm_list.append(VM(base=base)) - except Exception as e: + except ClanError as e: show_error_dialog(e) # start/end slices can be used for pagination diff --git a/pkgs/clan-vm-manager/clan_vm_manager/ui/clan_select_list.py b/pkgs/clan-vm-manager/clan_vm_manager/ui/clan_select_list.py index ddefe9d86..b7448dc7e 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/ui/clan_select_list.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/ui/clan_select_list.py @@ -1,9 +1,10 @@ from collections.abc import Callable -from gi.repository import GdkPixbuf, Gtk +from gi.repository import Gdk, GdkPixbuf, Gtk from ..interfaces import Callbacks from ..models import VMBase +from .context_menu import VmMenu class ClanEditForm(Gtk.ListBox): @@ -97,7 +98,6 @@ class ClanList(Gtk.Box): self.set_selected = set_selected self.show_toolbar = show_toolbar self.cbs = cbs - self.show_join = cbs.show_join self.selected_vm: VMBase | None = selected_vm @@ -228,6 +228,8 @@ class ClanListView(Gtk.Box): self.vms: list[VMBase] = vms self.on_select_row = on_select_row self.on_double_click = on_double_click + self.context_menu: VmMenu | None = None + store_types = VMBase.name_to_type_map().values() self.list_store = Gtk.ListStore(*store_types) @@ -241,6 +243,7 @@ class ClanListView(Gtk.Box): selection = self.tree_view.get_selection() selection.connect("changed", self._on_select_row) self.tree_view.connect("row-activated", self._on_double_click) + self.tree_view.connect("button-press-event", self._on_button_pressed) self.set_border_width(10) self.add(self.tree_view) @@ -272,12 +275,29 @@ class ClanListView(Gtk.Box): vm = VMBase(*model[row]) self.on_select_row(vm) + def _on_button_pressed( + self, tree_view: Gtk.TreeView, event: Gdk.EventButton + ) -> None: + if self.context_menu: + self.context_menu.destroy() + self.context_menu = None + + if event.button == 3: + path, column, x, y = tree_view.get_path_at_pos(event.x, event.y) + if path is not None: + vm = VMBase(*self.list_store[path[0]]) + print(event) + print(f"Right click on {vm.url}") + self.context_menu = VmMenu(vm) + self.context_menu.popup_at_pointer(event) + def _on_double_click( self, tree_view: Gtk.TreeView, path: Gtk.TreePath, column: Gtk.TreeViewColumn ) -> None: # Get the selection object of the tree view selection = tree_view.get_selection() model, row = selection.get_selected() + if row is not None: vm = VMBase(*model[row]) self.on_double_click(vm) diff --git a/pkgs/clan-vm-manager/clan_vm_manager/ui/context_menu.py b/pkgs/clan-vm-manager/clan_vm_manager/ui/context_menu.py new file mode 100644 index 000000000..fa288274b --- /dev/null +++ b/pkgs/clan-vm-manager/clan_vm_manager/ui/context_menu.py @@ -0,0 +1,39 @@ +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk + +from ..models import VMBase + + +class VmMenu(Gtk.Menu): + def __init__(self, vm: VMBase) -> None: + super().__init__() + self.vm = vm + self.menu_items = [ + ("Start", self.start_vm), + ("Stop", self.stop_vm), + ("Edit", self.edit_vm), + ("Remove", self.remove_vm), + ("Write to USB", self.write_to_usb), + ] + for item in self.menu_items: + menu_item = Gtk.MenuItem(label=item[0]) + menu_item.connect("activate", item[1]) + self.append(menu_item) + self.show_all() + + def start_vm(self, widget: Gtk.Widget) -> None: + print("start_vm") + + def stop_vm(self, widget: Gtk.Widget) -> None: + print("stop_vm") + + def edit_vm(self, widget: Gtk.Widget) -> None: + print("edit_vm") + + def remove_vm(self, widget: Gtk.Widget) -> None: + print("remove_vm") + + def write_to_usb(self, widget: Gtk.Widget) -> None: + print("write_to_usb")