forked from clan/clan-core
Merge pull request 'vm-manager: move signals to emitter' (#1271) from hsjobeki-vm-manager/empty-splash into main
This commit is contained in:
commit
39d6edc673
@ -1,6 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from typing import Any, ClassVar, Generic, TypeVar
|
from typing import Any, Generic, TypeVar
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
|
|
||||||
@ -24,10 +24,6 @@ class GKVStore(GObject.GObject, Gio.ListModel, Generic[K, V]):
|
|||||||
This class could be optimized by having the objects remember their position in the list.
|
This class could be optimized by having the objects remember their position in the list.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__gsignals__: ClassVar = {
|
|
||||||
"is_ready": (GObject.SignalFlags.RUN_FIRST, None, []),
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, gtype: type[V], key_gen: Callable[[V], K]) -> None:
|
def __init__(self, gtype: type[V], key_gen: Callable[[V], K]) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.gtype = gtype
|
self.gtype = gtype
|
||||||
|
@ -43,8 +43,11 @@ class EmptySplash(Gtk.Box):
|
|||||||
join_button = Gtk.Button(label="Join")
|
join_button = Gtk.Button(label="Join")
|
||||||
join_button.connect("clicked", self._on_join, join_entry)
|
join_button.connect("clicked", self._on_join, join_entry)
|
||||||
|
|
||||||
|
join_entry.connect("activate", lambda e: self._on_join(join_button, e))
|
||||||
|
|
||||||
clamp = Adw.Clamp()
|
clamp = Adw.Clamp()
|
||||||
clamp.set_maximum_size(400)
|
clamp.set_maximum_size(400)
|
||||||
|
clamp.set_margin_bottom(40)
|
||||||
vbox.append(empty_label)
|
vbox.append(empty_label)
|
||||||
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
|
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
|
||||||
hbox.append(join_entry)
|
hbox.append(join_entry)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any, ClassVar
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
from clan_cli.clan_uri import ClanURI
|
from clan_cli.clan_uri import ClanURI
|
||||||
@ -15,7 +15,7 @@ from clan_vm_manager.views.logs import Logs
|
|||||||
|
|
||||||
gi.require_version("GObject", "2.0")
|
gi.require_version("GObject", "2.0")
|
||||||
gi.require_version("Gtk", "4.0")
|
gi.require_version("Gtk", "4.0")
|
||||||
from gi.repository import Gio, GLib
|
from gi.repository import Gio, GLib, GObject
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -25,10 +25,18 @@ class VMStore(GKVStore):
|
|||||||
super().__init__(VMObject, lambda vm: vm.data.flake.flake_attr)
|
super().__init__(VMObject, lambda vm: vm.data.flake.flake_attr)
|
||||||
|
|
||||||
|
|
||||||
|
class Emitter(GObject.GObject):
|
||||||
|
__gsignals__: ClassVar = {
|
||||||
|
"is_ready": (GObject.SignalFlags.RUN_FIRST, None, []),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class ClanStore:
|
class ClanStore:
|
||||||
_instance: "None | ClanStore" = None
|
_instance: "None | ClanStore" = None
|
||||||
_clan_store: GKVStore[str, VMStore]
|
_clan_store: GKVStore[str, VMStore]
|
||||||
|
|
||||||
|
_emitter: Emitter
|
||||||
|
|
||||||
# set the vm that is outputting logs
|
# set the vm that is outputting logs
|
||||||
# build logs are automatically streamed to the logs-view
|
# build logs are automatically streamed to the logs-view
|
||||||
_logging_vm: VMObject | None = None
|
_logging_vm: VMObject | None = None
|
||||||
@ -44,9 +52,16 @@ class ClanStore:
|
|||||||
cls._clan_store = GKVStore(
|
cls._clan_store = GKVStore(
|
||||||
VMStore, lambda store: store.first().data.flake.flake_url
|
VMStore, lambda store: store.first().data.flake.flake_url
|
||||||
)
|
)
|
||||||
|
cls._emitter = Emitter()
|
||||||
|
|
||||||
return cls._instance
|
return cls._instance
|
||||||
|
|
||||||
|
def emit(self, signal: str) -> None:
|
||||||
|
self._emitter.emit(signal)
|
||||||
|
|
||||||
|
def connect(self, signal: str, cb: Callable[(...), Any]) -> None:
|
||||||
|
self._emitter.connect(signal, cb)
|
||||||
|
|
||||||
def set_logging_vm(self, ident: str) -> VMObject | None:
|
def set_logging_vm(self, ident: str) -> VMObject | None:
|
||||||
vm = self.get_vm(ClanURI(f"clan://{ident}"))
|
vm = self.get_vm(ClanURI(f"clan://{ident}"))
|
||||||
if vm is not None:
|
if vm is not None:
|
||||||
|
@ -74,11 +74,11 @@ class ClanList(Gtk.Box):
|
|||||||
self.join_boxed_list.add_css_class("join-list")
|
self.join_boxed_list.add_css_class("join-list")
|
||||||
self.append(self.join_boxed_list)
|
self.append(self.join_boxed_list)
|
||||||
|
|
||||||
clan_store = ClanStore.use().clan_store
|
clan_store = ClanStore.use()
|
||||||
clan_store.connect("is_ready", self.display_splash)
|
clan_store.connect("is_ready", self.display_splash)
|
||||||
|
|
||||||
self.group_list = create_boxed_list(
|
self.group_list = create_boxed_list(
|
||||||
model=clan_store, render_row=self.render_group_row
|
model=clan_store.clan_store, render_row=self.render_group_row
|
||||||
)
|
)
|
||||||
self.group_list.add_css_class("group-list")
|
self.group_list.add_css_class("group-list")
|
||||||
self.append(self.group_list)
|
self.append(self.group_list)
|
||||||
@ -338,7 +338,7 @@ class ClanList(Gtk.Box):
|
|||||||
|
|
||||||
def on_after_join(self, source: JoinValue) -> None:
|
def on_after_join(self, source: JoinValue) -> None:
|
||||||
ToastOverlay.use().add_toast_unique(
|
ToastOverlay.use().add_toast_unique(
|
||||||
SuccessToast(f"Added/updated {source.url.machine.name}").toast,
|
SuccessToast(f"Updated {source.url.machine.name}").toast,
|
||||||
"success.join",
|
"success.join",
|
||||||
)
|
)
|
||||||
# If the join request list is empty disable the shadow artefact
|
# If the join request list is empty disable the shadow artefact
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
from collections.abc import Callable
|
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
from clan_cli.history.list import list_history
|
from clan_cli.history.list import list_history
|
||||||
@ -42,9 +41,7 @@ class MainWindow(Adw.ApplicationWindow):
|
|||||||
self.tray_icon: TrayIcon = TrayIcon(app)
|
self.tray_icon: TrayIcon = TrayIcon(app)
|
||||||
|
|
||||||
# Initialize all ClanStore
|
# Initialize all ClanStore
|
||||||
threading.Thread(
|
threading.Thread(target=self._populate_vms).start()
|
||||||
target=self._populate_vms, args=[self._set_clan_store_ready]
|
|
||||||
).start()
|
|
||||||
|
|
||||||
# Initialize all views
|
# Initialize all views
|
||||||
stack_view = ViewStack.use().view
|
stack_view = ViewStack.use().view
|
||||||
@ -68,16 +65,17 @@ class MainWindow(Adw.ApplicationWindow):
|
|||||||
|
|
||||||
self.connect("destroy", self.on_destroy)
|
self.connect("destroy", self.on_destroy)
|
||||||
|
|
||||||
def _set_clan_store_ready(self) -> None:
|
def _set_clan_store_ready(self) -> bool:
|
||||||
ClanStore.use().clan_store.emit("is_ready")
|
ClanStore.use().emit("is_ready")
|
||||||
|
return GLib.SOURCE_REMOVE
|
||||||
|
|
||||||
def _populate_vms(self, done: Callable[[], None]) -> None:
|
def _populate_vms(self) -> None:
|
||||||
# Execute `clan flakes add <path>` to democlan for this to work
|
# Execute `clan flakes add <path>` to democlan for this to work
|
||||||
# TODO: Make list_history a generator function
|
# TODO: Make list_history a generator function
|
||||||
for entry in list_history():
|
for entry in list_history():
|
||||||
GLib.idle_add(ClanStore.use().create_vm_task, entry)
|
GLib.idle_add(ClanStore.use().create_vm_task, entry)
|
||||||
|
|
||||||
GLib.idle_add(done)
|
GLib.idle_add(self._set_clan_store_ready)
|
||||||
|
|
||||||
def kill_vms(self) -> None:
|
def kill_vms(self) -> None:
|
||||||
log.debug("Killing all VMs")
|
log.debug("Killing all VMs")
|
||||||
|
Loading…
Reference in New Issue
Block a user