clan-vm-manager: add log view
This commit is contained in:
@@ -57,3 +57,10 @@ avatar {
|
|||||||
searchbar {
|
searchbar {
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.log-view {
|
||||||
|
margin-top: 12px;
|
||||||
|
font-family: monospace;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,16 +8,21 @@ gi.require_version("Adw", "1")
|
|||||||
|
|
||||||
from gi.repository import Adw
|
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__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ToastOverlay:
|
class ToastOverlay:
|
||||||
"""
|
"""
|
||||||
The ToastOverlay is a class that manages the display of toasts
|
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
|
It should be used as a singleton in your application to prevent duplicate toasts
|
||||||
Usage
|
Usage
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# For some reason, the adw toast overlay cannot be subclassed
|
# 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
|
overlay: Adw.ToastOverlay
|
||||||
active_toasts: set[str]
|
active_toasts: set[str]
|
||||||
|
|
||||||
@@ -45,10 +50,20 @@ class ToastOverlay:
|
|||||||
class ErrorToast:
|
class ErrorToast:
|
||||||
toast: Adw.Toast
|
toast: Adw.Toast
|
||||||
|
|
||||||
def __init__(self, message: str):
|
def __init__(self, message: str) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.toast = Adw.Toast.new(f"Error: {message}")
|
self.toast = Adw.Toast.new(f"Error: {message}")
|
||||||
self.toast.set_priority(Adw.ToastPriority.HIGH)
|
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"),
|
||||||
|
)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ from functools import partial
|
|||||||
from typing import Any, TypeVar
|
from typing import Any, TypeVar
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
from clan_cli import history
|
|
||||||
from clan_cli.clan_uri import ClanURI
|
from clan_cli.clan_uri import ClanURI
|
||||||
|
|
||||||
from clan_vm_manager.components.interfaces import ClanConfig
|
from clan_vm_manager.components.interfaces import ClanConfig
|
||||||
@@ -57,7 +56,6 @@ class ClanList(Gtk.Box):
|
|||||||
app.connect("join_request", self.on_join_request)
|
app.connect("join_request", self.on_join_request)
|
||||||
|
|
||||||
self.log_label: Gtk.Label = Gtk.Label()
|
self.log_label: Gtk.Label = Gtk.Label()
|
||||||
self.__init_machines = history.add.list_history()
|
|
||||||
|
|
||||||
# Add join list
|
# Add join list
|
||||||
self.join_boxed_list = create_boxed_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 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
|
||||||
box.set_valign(Gtk.Align.CENTER)
|
box.set_valign(Gtk.Align.CENTER)
|
||||||
|
|
||||||
|
|
||||||
add_button = Gtk.Button()
|
add_button = Gtk.Button()
|
||||||
add_button_content = Adw.ButtonContent.new()
|
add_button_content = Adw.ButtonContent.new()
|
||||||
add_button_content.set_label("Add machine")
|
add_button_content.set_label("Add machine")
|
||||||
@@ -104,7 +101,6 @@ class ClanList(Gtk.Box):
|
|||||||
add_button.add_css_class("flat")
|
add_button.add_css_class("flat")
|
||||||
add_button.set_child(add_button_content)
|
add_button.set_child(add_button_content)
|
||||||
|
|
||||||
|
|
||||||
# add_button.set_has_frame(False)
|
# add_button.set_has_frame(False)
|
||||||
# add_button.set_menu_model(menu_model)
|
# add_button.set_menu_model(menu_model)
|
||||||
# add_button.set_label("Add machine")
|
# add_button.set_label("Add machine")
|
||||||
@@ -217,7 +213,10 @@ class ClanList(Gtk.Box):
|
|||||||
sub = row.get_subtitle()
|
sub = row.get_subtitle()
|
||||||
assert sub is not None
|
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(
|
row.set_subtitle(
|
||||||
sub + "\nClan already exists. Joining again will update it"
|
sub + "\nClan already exists. Joining again will update it"
|
||||||
|
|||||||
69
pkgs/clan-vm-manager/clan_vm_manager/views/logs.py
Normal file
69
pkgs/clan-vm-manager/clan_vm_manager/views/logs.py
Normal 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)
|
||||||
@@ -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.singletons.use_vms import ClanStore
|
||||||
from clan_vm_manager.views.details import Details
|
from clan_vm_manager.views.details import Details
|
||||||
from clan_vm_manager.views.list import ClanList
|
from clan_vm_manager.views.list import ClanList
|
||||||
|
from clan_vm_manager.views.logs import Logs
|
||||||
|
|
||||||
gi.require_version("Adw", "1")
|
gi.require_version("Adw", "1")
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ class MainWindow(Adw.ApplicationWindow):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.set_title("cLAN Manager")
|
self.set_title("cLAN Manager")
|
||||||
self.set_default_size(980, 650)
|
self.set_default_size(980, 650)
|
||||||
|
|
||||||
overlay = ToastOverlay.use().overlay
|
overlay = ToastOverlay.use().overlay
|
||||||
view = Adw.ToolbarView()
|
view = Adw.ToolbarView()
|
||||||
overlay.set_child(view)
|
overlay.set_child(view)
|
||||||
@@ -52,6 +53,7 @@ class MainWindow(Adw.ApplicationWindow):
|
|||||||
|
|
||||||
stack_view.add_named(scroll, "list")
|
stack_view.add_named(scroll, "list")
|
||||||
stack_view.add_named(Details(), "details")
|
stack_view.add_named(Details(), "details")
|
||||||
|
stack_view.add_named(Logs(), "logs")
|
||||||
|
|
||||||
stack_view.set_visible_child_name(config.initial_view)
|
stack_view.set_visible_child_name(config.initial_view)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user