clan-vm-manager: add log view
All checks were successful
checks / check-links (pull_request) Successful in 22s
checks / checks-impure (pull_request) Successful in 2m9s
checks / checks (pull_request) Successful in 3m37s

This commit is contained in:
Johannes Kirschbauer 2024-03-10 13:18:01 +01:00
parent ee8fa1da0a
commit 4687c816ab
Signed by: hsjobeki
SSH Key Fingerprint: SHA256:vX3utDqig7Ph5L0JPv87ZTPb/w7cMzREKVZzzLFg9qU
5 changed files with 101 additions and 9 deletions

View File

@ -57,3 +57,10 @@ avatar {
searchbar {
margin-bottom: 25px;
}
.log-view {
margin-top: 12px;
font-family: monospace;
padding: 8px;
}

View File

@ -8,16 +8,21 @@ gi.require_version("Adw", "1")
from gi.repository import Adw
from clan_vm_manager.singletons.use_views import ViewStack
from clan_vm_manager.views.logs import Logs
log = logging.getLogger(__name__)
class ToastOverlay:
"""
The ToastOverlay is a class that manages the display of toasts
It should be used as a singleton in your application to prevent duplicate toasts
Usage
"""
# For some reason, the adw toast overlay cannot be subclassed
# Thats why it is added as a class property
# Thats why it is added as a class property
overlay: Adw.ToastOverlay
active_toasts: set[str]
@ -45,10 +50,20 @@ class ToastOverlay:
class ErrorToast:
toast: Adw.Toast
def __init__(self, message: str):
def __init__(self, message: str) -> None:
super().__init__()
self.toast = Adw.Toast.new(f"Error: {message}")
self.toast.set_priority(Adw.ToastPriority.HIGH)
self.toast.set_button_label("details")
views = ViewStack.use().view
# we cannot check this type, python is not smart enough
logs_view: Logs = views.get_child_by_name("logs") # type: ignore
logs_view.set_message(message)
self.toast.connect(
"button-clicked",
lambda _: views.set_visible_child_name("logs"),
)

View File

@ -4,7 +4,6 @@ from functools import partial
from typing import Any, TypeVar
import gi
from clan_cli import history
from clan_cli.clan_uri import ClanURI
from clan_vm_manager.components.interfaces import ClanConfig
@ -57,7 +56,6 @@ class ClanList(Gtk.Box):
app.connect("join_request", self.on_join_request)
self.log_label: Gtk.Label = Gtk.Label()
self.__init_machines = history.add.list_history()
# Add join list
self.join_boxed_list = create_boxed_list(
@ -96,7 +94,6 @@ class ClanList(Gtk.Box):
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
box.set_valign(Gtk.Align.CENTER)
add_button = Gtk.Button()
add_button_content = Adw.ButtonContent.new()
add_button_content.set_label("Add machine")
@ -104,7 +101,6 @@ class ClanList(Gtk.Box):
add_button.add_css_class("flat")
add_button.set_child(add_button_content)
# add_button.set_has_frame(False)
# add_button.set_menu_model(menu_model)
# add_button.set_label("Add machine")
@ -217,7 +213,10 @@ class ClanList(Gtk.Box):
sub = row.get_subtitle()
assert sub is not None
ToastOverlay.use().add_toast_unique(ErrorToast("Already exists. Joining again will update it").toast, "warning.duplicate.join")
ToastOverlay.use().add_toast_unique(
ErrorToast("Already exists. Joining again will update it").toast,
"warning.duplicate.join",
)
row.set_subtitle(
sub + "\nClan already exists. Joining again will update it"

View File

@ -0,0 +1,69 @@
import logging
import gi
gi.require_version("Adw", "1")
from gi.repository import Adw, Gio, Gtk
from clan_vm_manager.singletons.use_views import ViewStack
log = logging.getLogger(__name__)
class Logs(Gtk.Box):
"""
Simple log view
This includes a banner and a text view and a button to close the log and navigate back to the overview
"""
def __init__(self) -> None:
super().__init__(orientation=Gtk.Orientation.VERTICAL)
app = Gio.Application.get_default()
assert app is not None
self.banner = Adw.Banner.new("Error details")
self.banner.set_revealed(True)
close_button = Gtk.Button()
button_content = Adw.ButtonContent.new()
button_content.set_label("Back")
button_content.set_icon_name("go-previous-symbolic")
close_button.add_css_class("flat")
close_button.set_child(button_content)
close_button.connect(
"clicked",
lambda _: ViewStack.use().view.set_visible_child_name("list"),
)
self.close_button = close_button
self.text_view = Gtk.TextView()
self.text_view.set_editable(False)
self.text_view.set_wrap_mode(Gtk.WrapMode.WORD)
self.text_view.add_css_class("log-view")
self.append(self.close_button)
self.append(self.banner)
self.append(self.text_view)
def set_message(self, message: str) -> None:
"""
Set the log message. This will delete any previous message
"""
buffer = self.text_view.get_buffer()
buffer.set_text(message)
mark = buffer.create_mark(None, buffer.get_end_iter(), False) # type: ignore
self.text_view.scroll_to_mark(mark, 0.05, True, 0.0, 1.0)
def append_message(self, message: str) -> None:
"""
Append to the end of a potentially existent log message
"""
buffer = self.text_view.get_buffer()
end_iter = buffer.get_end_iter()
buffer.insert(end_iter, message) # type: ignore
mark = buffer.create_mark(None, buffer.get_end_iter(), False) # type: ignore
self.text_view.scroll_to_mark(mark, 0.05, True, 0.0, 1.0)

View File

@ -10,6 +10,7 @@ from clan_vm_manager.singletons.use_views import ViewStack
from clan_vm_manager.singletons.use_vms import ClanStore
from clan_vm_manager.views.details import Details
from clan_vm_manager.views.list import ClanList
from clan_vm_manager.views.logs import Logs
gi.require_version("Adw", "1")
@ -25,7 +26,7 @@ class MainWindow(Adw.ApplicationWindow):
super().__init__()
self.set_title("cLAN Manager")
self.set_default_size(980, 650)
overlay = ToastOverlay.use().overlay
view = Adw.ToolbarView()
overlay.set_child(view)
@ -52,6 +53,7 @@ class MainWindow(Adw.ApplicationWindow):
stack_view.add_named(scroll, "list")
stack_view.add_named(Details(), "details")
stack_view.add_named(Logs(), "logs")
stack_view.set_visible_child_name(config.initial_view)