diff --git a/pkgs/clan-vm-manager/clan_vm_manager/app.py b/pkgs/clan-vm-manager/clan_vm_manager/app.py index 3fa69a3c..3c9b7ec3 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/app.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/app.py @@ -6,19 +6,32 @@ from typing import Any import gi +from clan_vm_manager.models import VMBase + gi.require_version("Gtk", "3.0") from gi.repository import Gio, Gtk from .constants import constants -from .ui.clan_select_list import ClanSelectPage +from .ui.clan_select_list import ClanEdit, ClanList class ClanJoinPage(Gtk.Box): - def __init__(self) -> None: + def __init__(self, stack: Gtk.Stack) -> None: super().__init__() - self.page = Gtk.Box() + self.page = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=6, expand=True + ) self.set_border_width(10) - self.add(Gtk.Label(label="Join")) + self.stack = stack + + button = Gtk.Button(label="Back to list", margin_left=10) + button.connect("clicked", self.switch) + self.add(button) + + self.add(Gtk.Label("Join cLan")) + + def switch(self, widget: Gtk.Widget) -> None: + self.stack.set_visible_child_name("list") class MainWindow(Gtk.ApplicationWindow): @@ -35,25 +48,46 @@ class MainWindow(Gtk.ApplicationWindow): # Add a notebook layout # https://python-gtk-3-tutorial.readthedocs.io/en/latest/layout.html#notebook self.notebook = Gtk.Notebook() - vbox.add(self.notebook) + self.stack = Gtk.Stack() + # self.stack_switcher = Gtk.StackSwitcher() - self.notebook.append_page( - ClanSelectPage(self.reload_clan_tab), Gtk.Label(label="Overview") + # Add named stacks + self.stack.add_titled( + ClanList(self.show_list, self.show_edit, self.set_selected), "list", "List" ) - self.notebook.append_page(ClanJoinPage(), Gtk.Label(label="Join")) + self.stack.add_titled(ClanJoinPage(self.show_list), "join", "Join") + self.stack.add_titled(ClanEdit(self.show_list, None), "edit", "Edit") + + vbox.add(self.stack) # Must be called AFTER all components were added self.show_all() - def reload_clan_tab(self) -> None: - print("Remounting ClanSelectPage") - self.notebook.remove_page(0) - self.notebook.insert_page( - ClanSelectPage(self.reload_clan_tab), Gtk.Label(label="Overview2"), 0 + def set_selected(self, sel: VMBase | None) -> None: + self.selected = sel + print(f"APP selected + {self.selected}") + + def show_list(self) -> None: + widget = self.stack.get_child_by_name("list") + print("Remounting ClanListView") + if widget: + widget.destroy() + + self.stack.add_titled( + ClanList(self.show_list, self.show_edit, self.set_selected), "list", "List" ) - # must call show_all before set active tab self.show_all() - self.notebook.set_current_page(0) + self.stack.set_visible_child_name("list") + + def show_edit(self) -> None: + print("Remounting ClanEdit") + widget = self.stack.get_child_by_name("edit") + if widget: + widget.destroy() + + self.stack.add_titled(ClanEdit(self.show_list, self.selected), "edit", "Edit") + self.show_all() + self.stack.set_visible_child_name("edit") def on_quit(self, *args: Any) -> None: Gio.Application.quit(self.get_application()) 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 a88682c6..50af0ebf 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 @@ -5,10 +5,93 @@ from gi.repository import GdkPixbuf, Gtk from ..models import VMBase, get_initial_vms -class ClanSelectPage(Gtk.Box): - def __init__(self, reload: Callable[[], None]) -> None: +class ClanEditForm(Gtk.ListBox): + def __init__(self, selected: VMBase | None) -> None: + super().__init__() + self.page = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, expand=True) + self.set_border_width(10) + self.selected = selected + self.set_selection_mode(0) + + if self.selected: + row = Gtk.ListBoxRow() + row.add(Gtk.Label(f"\n {self.selected.name}")) + self.add(row) + + # ---------- row 1 -------- + row = Gtk.ListBoxRow() + row_layout = Gtk.Box(spacing=6, expand=True) + + # Doc: pack_start/end takes alignment params Expand, Fill, Padding + row_layout.pack_start(Gtk.Label("Memory Size in MiB"), False, False, 5) + row_layout.pack_start( + Gtk.SpinButton.new_with_range(512, 4096, 256), True, True, 0 + ) + + row.add(row_layout) + self.add(row) + + # ----------- row 2 ------- + + row = Gtk.ListBoxRow() + row_layout = Gtk.Box(spacing=6, expand=True) + + row_layout.pack_start(Gtk.Label("CPU Count"), False, False, 5) + row_layout.pack_end(Gtk.SpinButton.new_with_range(1, 5, 1), True, True, 0) + + row.add(row_layout) + self.add(row) + + def switch(self, widget: Gtk.Widget) -> None: + self.show_list() + + +class ClanEdit(Gtk.Box): + def __init__(self, show_list: Callable[[], None], selected: VMBase | None) -> None: super().__init__(orientation=Gtk.Orientation.VERTICAL, expand=True) + self.show_list = show_list + self.selected = selected + + button_hooks = { + "on_save_clicked": self.on_save, + } + + self.toolbar = ClanEditToolbar(**button_hooks) + self.add(self.toolbar) + self.add(ClanEditForm(self.selected)) + + def on_save(self, widget: Gtk.Widget) -> None: + print("Save clicked saving values") + self.show_list() + + +class ClanList(Gtk.Box): + """ + The ClanList + Is the composition of + the ClanListToolbar + the clanListView + # ------------------------# + # - Tools < Edit> # + # ------------------------# + # - List Items + # - <...> + # ------------------------# + """ + + def __init__( + self, + show_list: Callable[[], None], + show_edit: Callable[[], None], + set_selected: Callable[[VMBase | None], None], + ) -> None: + super().__init__(orientation=Gtk.Orientation.VERTICAL, expand=True) + + self.show_edit = show_edit + self.show_list = show_list + self.set_selected = set_selected + # TODO: We should use somekind of useState hook here. # that updates the list of VMs when the user changes something # @hsjobeki reply: @qubasa: This is how to update data in the list store @@ -17,59 +100,89 @@ class ClanSelectPage(Gtk.Box): # This class needs to take ownership of the data because it has access to the listStore only self.selected_vm: VMBase | None = None - self.list_hooks = { - "on_select_row": self.on_select_vm, - } - self.add(ClanSelectList(**self.list_hooks)) - self.reload = reload button_hooks = { "on_start_clicked": self.on_start_clicked, "on_stop_clicked": self.on_stop_clicked, - "on_backup_clicked": self.on_backup_clicked, + "on_edit_clicked": self.on_edit_clicked, } - self.add(ClanSelectButtons(**button_hooks)) + self.toolbar = ClanListToolbar(**button_hooks) + self.toolbar.set_is_selected(False) + self.add(self.toolbar) + + self.list_hooks = { + "on_select_row": self.on_select_vm, + } + self.add(ClanListView(**self.list_hooks)) def on_start_clicked(self, widget: Gtk.Widget) -> None: print("Start clicked") if self.selected_vm: self.selected_vm.run() - self.reload() + # Call this to reload + self.show_list() def on_stop_clicked(self, widget: Gtk.Widget) -> None: print("Stop clicked") - def on_backup_clicked(self, widget: Gtk.Widget) -> None: - print("Backup clicked") + def on_edit_clicked(self, widget: Gtk.Widget) -> None: + print("Edit clicked") + self.show_edit() def on_select_vm(self, vm: VMBase) -> None: print(f"on_select_vm: {vm}") + if vm is None: + self.toolbar.set_is_selected(False) + else: + self.toolbar.set_is_selected(True) + + self.set_selected(vm) self.selected_vm = vm -class ClanSelectButtons(Gtk.Box): +class ClanListToolbar(Gtk.Toolbar): def __init__( self, *, on_start_clicked: Callable[[Gtk.Widget], None], on_stop_clicked: Callable[[Gtk.Widget], None], - on_backup_clicked: Callable[[Gtk.Widget], None], + on_edit_clicked: Callable[[Gtk.Widget], None], ) -> None: - super().__init__( - orientation=Gtk.Orientation.HORIZONTAL, margin_bottom=10, margin_top=10 - ) + super().__init__(orientation=Gtk.Orientation.HORIZONTAL) - button = Gtk.Button(label="Start", margin_left=10) - button.connect("clicked", on_start_clicked) - self.add(button) - button = Gtk.Button(label="Stop", margin_left=10) - button.connect("clicked", on_stop_clicked) - self.add(button) - button = Gtk.Button(label="Edit", margin_left=10) - button.connect("clicked", on_backup_clicked) - self.add(button) + self.start_button = Gtk.ToolButton(label="Join") + self.start_button.connect("clicked", on_start_clicked) + self.add(self.start_button) + + self.edit_button = Gtk.ToolButton(label="Edit") + self.edit_button.connect("clicked", on_edit_clicked) + self.add(self.edit_button) + + def set_is_selected(self, s: bool) -> None: + if s: + self.edit_button.set_sensitive(True) + self.start_button.set_sensitive(True) + else: + self.edit_button.set_sensitive(False) + self.start_button.set_sensitive(False) -class ClanSelectList(Gtk.Box): +class ClanEditToolbar(Gtk.Toolbar): + def __init__( + self, + *, + on_save_clicked: Callable[[Gtk.Widget], None], + ) -> None: + super().__init__(orientation=Gtk.Orientation.HORIZONTAL) + + # Icons See: https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html + # Could not find a suitable one + self.save_button = Gtk.ToolButton(label="Save") + self.save_button.connect("clicked", on_save_clicked) + + self.add(self.save_button) + + +class ClanListView(Gtk.Box): def __init__( self, *,