chore: normalize dialogs

This commit is contained in:
aclist committed 2026-01-12 03:44:41 +09:00
1 parent 45bd0e16ec
commit 1bc21d6083
15 files changed
+283 -282

No files matched your search

+149 -120
View File
@@ -4,147 +4,103 @@ from typing import Any, Literal, Self, TYPE_CHECKING
from dzgui.const.constants import NO_EXPAND, NO_FILL, EXPAND, FILL
from dzgui.const.enum import Popup, ButtonType, NotebookPage
from dzgui.util import strings
from dzgui.views.components.buttons import ClipboardButton
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa E402
if TYPE_CHECKING:
from dzgui.controllers.mc import Controller
class ExceptionDialog(Gtk.MessageDialog):
def __init__(self, controller: "Controller", trace: str):
super().__init__(
transient_for=controller.mediator.window,
message_type=Gtk.MessageType.ERROR,
buttons=Gtk.ButtonsType.OK,
text="Error",
secondary_text="Something went wrong. See the detailed error below.",
title=strings.dialog_header,
modal=True,
)
# TODO: strings
self.set_size_request(550, 250)
from dzgui.views.components.buttons import ClipboardButton
content = self.get_content_area()
content.set_spacing(0)
box = Gtk.Box(hexpand=True, vexpand=True, orientation=Gtk.Orientation.VERTICAL)
textview = Gtk.TextView(wrap_mode=Gtk.WrapMode.WORD, editable=False, left_margin=10, right_margin=10)
textview.set_buffer(Gtk.TextBuffer(text=trace))
# NOTE: box expands to end of content area
box.pack_start(textview, EXPAND, FILL, 10)
action_area = self.get_action_area()
action_area.set_spacing(10)
action_area.set_margin_bottom(10)
action_area.set_layout(Gtk.ButtonBoxStyle.CENTER)
but = ClipboardButton(controller, trace)
# TODO: flag to set button with text, or other class
but.set_label("Copy")
action_area.pack_start(but, True, True, 10)
# NOTE: reverse button order after insertion
action_area.set_direction(Gtk.TextDirection.RTL)
content.add(box)
self.set_default_response(Gtk.ResponseType.OK)
self.connect("response", self._on_response)
self.show_all()
"""
usage:
from dzgui.views.dialogs.generic import ExceptionDialog
try:
a.banana()
except Exception as e:
trace = traceback.format_exc()
dialog = ExceptionDialog(MainController, trace)
dialog.run()
"""
def _on_response(self, dialog: Self, response: Gtk.ResponseType) -> None:
self.destroy()
# TODO: reimplement as standalone dialogs
# NOTE: steam deck prints <2> if dialog title is same as window title
# case Popup.MODLIST:
# dialog_type = Gtk.MessageType.INFO
# button_type = Gtk.ButtonsType.OK
# header_text = strings.modlist
# case Popup.DETAILS:
# dialog_type = Gtk.MessageType.INFO
# button_type = Gtk.ButtonsType.OK
# header_text = strings.server_details
# TODO: unused
# def update_label(self, text: str) -> None:
# self.format_secondary_text(text)
class GenericDialog(Gtk.MessageDialog):
def __init__(self, controller: "Controller", text: str, mode: Popup):
match mode:
case Popup.WAIT:
dialog_type = Gtk.MessageType.INFO
button_type = Gtk.ButtonsType.NONE
header_text = strings.wait
case Popup.NOTIFY | Popup.RETURN | Popup.QUIT:
dialog_type = Gtk.MessageType.INFO
button_type = Gtk.ButtonsType.OK
header_text = strings.notice
case Popup.CONFIRM:
dialog_type = Gtk.MessageType.QUESTION
button_type = Gtk.ButtonsType.OK_CANCEL
header_text = strings.confirmation
case Popup.ENTRY:
dialog_type = Gtk.MessageType.QUESTION
button_type = Gtk.ButtonsType.OK_CANCEL
header_text = strings.input_required
case Popup.MODLIST:
dialog_type = Gtk.MessageType.INFO
button_type = Gtk.ButtonsType.OK
header_text = strings.modlist
case Popup.DETAILS:
dialog_type = Gtk.MessageType.INFO
button_type = Gtk.ButtonsType.OK
header_text = strings.server_details
Gtk.MessageDialog.__init__(
self,
def __init__(self,
controller: "Controller",
text: str,
mtype: Gtk.MessageType,
buttons: Gtk.ButtonsType,
secondary: str
) -> None:
super().__init__(
transient_for=controller.mediator.window,
message_type=dialog_type,
buttons=button_type,
text=header_text,
secondary_text=textwrap.fill(text, 50),
# NOTE: steam deck prints <2> if dialog title is same as window title
message_type=mtype,
buttons=buttons,
text=text,
secondary_text=secondary,
title=strings.dialog_header,
modal=True,
)
self.controller = controller
if mode == Popup.WAIT:
dialogBox = self.get_content_area()
spinner = Gtk.Spinner()
dialogBox.pack_end(spinner, NO_EXPAND, NO_FILL, 0)
spinner.start()
self.connect("delete-event", self._on_dialog_delete)
if mode == Popup.RETURN:
button_label = strings.main_menu
ok = self.action_area.get_children()[0]
ok.set_label(button_label)
ok.connect("clicked", self._return_to_main_menu)
self.connect("delete-event", self._return_to_main_menu)
if mode == Popup.QUIT:
button_label = strings.exit_app
ok = self.action_area.get_children()[0]
ok.set_label(button_label)
ok.connect("clicked", self._quit)
self.connect("delete-event", self._quit)
self.set_default_response(Gtk.ResponseType.OK)
self.set_size_request(500, 0)
self.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
self.set_default_response(Gtk.ResponseType.OK)
self.connect("response", self._on_response)
self.action_area.set_layout(Gtk.ButtonBoxStyle.CENTER)
self.action_area.set_margin_bottom(20)
self.outer = self.get_content_area()
self.outer.set_margin_start(30)
self.outer.set_margin_end(30)
def _quit(self, *args: Any) -> None:
self.controller.save_res_and_quit()
def _on_response(self, dialog: Self, response: Gtk.ResponseType) -> None:
self.destroy()
class ConfirmationDialog(GenericDialog):
def __init__(self, controller: "Controller", secondary: str):
super().__init__(
controller=controller,
text=strings.confirm,
mtype=Gtk.MessageType.QUESTION,
buttons=Gtk.ButtonsType.OK_CANCEL,
secondary=secondary,
)
class NotifyDialog(GenericDialog):
def __init__(self, controller: "Controller", secondary: str):
super().__init__(
controller=controller,
text=strings.notice,
mtype=Gtk.MessageType.INFO,
buttons=Gtk.ButtonsType.OK,
secondary=secondary,
)
class WaitDialog(GenericDialog):
def __init__(self, controller: "Controller", secondary: str):
super().__init__(
controller=controller,
text=strings.wait,
mtype=Gtk.MessageType.INFO,
buttons=Gtk.ButtonsType.NONE,
secondary=secondary,
)
self.connect("delete-event", self._on_dialog_delete)
content = self.get_content_area()
spinner = Gtk.Spinner()
content.pack_end(spinner, NO_EXPAND, NO_FILL, 0)
spinner.start()
self.show_all()
def _on_dialog_delete(
self, response_id: Gtk.ResponseType, event: Gdk.Event
@@ -154,8 +110,81 @@ class GenericDialog(Gtk.MessageDialog):
"""
return True
def _return_to_main_menu(self, widget: Gtk.Widget) -> None:
self.controller.open_page(NotebookPage.MAIN)
def update_label(self, text: str) -> None:
self.format_secondary_text(text)
class QuitDialog(GenericDialog):
def __init__(self, controller: "Controller", secondary: str):
super().__init__(
controller=controller,
text=strings.wait,
mtype=Gtk.MessageType.INFO,
buttons=Gtk.ButtonsType.NONE,
secondary=secondary,
)
self.controller = controller
self.add_button(strings.exit_app, Gtk.ResponseType.OK)
self.connect("response", self._on_response)
self.connect("delete-event", self._on_response)
def _on_response(self, dialog: Self, response: Gtk.ResponseType) -> None:
self.controller.save_res_and_quit()
class ExceptionDialog(GenericDialog):
"""
Error dialog with rich traceback.
Usage:
try:
foo()
except Exception:
trace = traceback.format_exc()
dialog = ExceptionDialog(Controller, trace)
dialog.run()
"""
def __init__(self, controller: "Controller", trace: str):
super().__init__(
controller=controller,
text=strings.error_heading,
mtype=Gtk.MessageType.ERROR,
buttons=Gtk.ButtonsType.NONE,
secondary=strings.something_wrong,
)
# NOTE: box expands to end of content area
scrollable = Gtk.ScrolledWindow(
propagate_natural_height=True,
max_content_height=500
)
box = Gtk.Box(hexpand=True, vexpand=True, orientation=Gtk.Orientation.VERTICAL)
textview = Gtk.TextView(
wrap_mode=Gtk.WrapMode.WORD,
editable=False,
left_margin=10,
right_margin=10
)
textview.set_buffer(Gtk.TextBuffer(text=trace))
box.pack_start(textview, EXPAND, FILL, 10)
scrollable.add(box)
content = self.get_content_area()
content.set_spacing(0)
# FIXME: padding around top of content area when traceback is long
content.add(scrollable)
copy_button = ClipboardButton(controller, trace)
self.add_action_widget(copy_button, Gtk.ResponseType.NONE)
self.add_button("OK", Gtk.ResponseType.OK)
action_area = self.get_action_area()
self.show_all()
self.connect("response", self._on_response)
def _on_response(self, dialog: Self, response: Gtk.ResponseType) -> None:
match response:
case Gtk.ResponseType.OK:
self.destroy()
case Gtk.ResponseType.NONE:
return True # type: ignore
case Gtk.ResponseType.DELETE_EVENT:
self.destroy()