1
0
forked from clan/clan-core

vm-manager: add toast overlay to main window

This commit is contained in:
Johannes Kirschbauer 2024-03-09 16:45:54 +01:00
parent bfa6ff6085
commit ee8fa1da0a
Signed by: hsjobeki
SSH Key Fingerprint: SHA256:vX3utDqig7Ph5L0JPv87ZTPb/w7cMzREKVZzzLFg9qU
5 changed files with 84 additions and 7 deletions

View File

@ -86,3 +86,9 @@ Here are some important documentation links related to the Clan VM Manager:
- [Python + GTK3 Tutorial](https://python-gtk-3-tutorial.readthedocs.io/en/latest/textview.html): Although the Clan VM Manager uses GTK4, this tutorial for GTK3 can still be useful as it covers the basics of building GTK-based applications with Python. It includes examples and explanations for various GTK widgets, including text views.
- [GNOME Human Interface Guidelines](https://developer.gnome.org/hig/): This link provides the GNOME Human Interface Guidelines, which offer design and usability recommendations for creating GNOME applications. It covers topics such as layout, navigation, and interaction patterns.
## Error handling
> Error dialogs should be avoided where possible, since they are disruptive.
>
> For simple non-critical errors, toasts can be a good alternative.

View File

@ -17,6 +17,7 @@ avatar {
}
.join-list {
margin-top: 1px;
margin-left: 2px;
margin-right: 2px;

View File

@ -0,0 +1,54 @@
import logging
from typing import Any
import gi
gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
from gi.repository import Adw
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
overlay: Adw.ToastOverlay
active_toasts: set[str]
_instance: "None | ToastOverlay" = None
def __init__(self) -> None:
raise RuntimeError("Call use() instead")
@classmethod
def use(cls: Any) -> "ToastOverlay":
if cls._instance is None:
cls._instance = cls.__new__(cls)
cls.overlay = Adw.ToastOverlay()
cls.active_toasts = set()
return cls._instance
def add_toast_unique(self, toast: Adw.Toast, key: str) -> None:
if key not in self.active_toasts:
self.active_toasts.add(key)
self.overlay.add_toast(toast)
toast.connect("dismissed", lambda toast: self.active_toasts.remove(key))
class ErrorToast:
toast: Adw.Toast
def __init__(self, message: str):
super().__init__()
self.toast = Adw.Toast.new(f"Error: {message}")
self.toast.set_priority(Adw.ToastPriority.HIGH)

View File

@ -9,6 +9,7 @@ from clan_cli.clan_uri import ClanURI
from clan_vm_manager.components.interfaces import ClanConfig
from clan_vm_manager.components.vmobj import VMObject
from clan_vm_manager.singletons.toast import ErrorToast, ToastOverlay
from clan_vm_manager.singletons.use_join import JoinList, JoinValue
from clan_vm_manager.singletons.use_vms import ClanStore, VMStore
@ -86,7 +87,7 @@ class ClanList(Gtk.Box):
assert app is not None
app.add_action(add_action)
menu_model = Gio.Menu()
# menu_model = Gio.Menu()
# TODO: Make this lazy, blocks UI startup for too long
# for vm in machines.list.list_machines(flake_url=vm.data.flake.flake_url):
# if vm not in vm_store:
@ -95,10 +96,18 @@ class ClanList(Gtk.Box):
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
box.set_valign(Gtk.Align.CENTER)
add_button = Gtk.MenuButton()
add_button.set_has_frame(False)
add_button.set_menu_model(menu_model)
add_button.set_label("Add machine")
add_button = Gtk.Button()
add_button_content = Adw.ButtonContent.new()
add_button_content.set_label("Add machine")
add_button_content.set_icon_name("list-add-symbolic")
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")
box.append(add_button)
grp.set_header_suffix(box)
@ -207,6 +216,9 @@ class ClanList(Gtk.Box):
if vm is not None:
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")
row.set_subtitle(
sub + "\nClan already exists. Joining again will update it"
)

View File

@ -5,6 +5,7 @@ import gi
from clan_cli.history.list import list_history
from clan_vm_manager.components.interfaces import ClanConfig
from clan_vm_manager.singletons.toast import ToastOverlay
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
@ -24,9 +25,12 @@ class MainWindow(Adw.ApplicationWindow):
super().__init__()
self.set_title("cLAN Manager")
self.set_default_size(980, 650)
overlay = ToastOverlay.use().overlay
view = Adw.ToolbarView()
self.set_content(view)
overlay.set_child(view)
self.set_content(overlay)
header = Adw.HeaderBar()
view.add_top_bar(header)