mirror of
https://github.com/aclist/dztui.git
synced 2026-10-30 14:42:54 +01:00
133 lines
4.6 KiB
Python
133 lines
4.6 KiB
Python
import logging
|
|
import re
|
|
import textwrap
|
|
|
|
from typing import TYPE_CHECKING
|
|
from importlib import resources
|
|
|
|
from dzgui.const.constants import APP_NAME, APP_NAME_LOWER, CHANGELOG_PATH
|
|
from dzgui.strings import changelog as clog
|
|
from dzgui.util.strings import missing_changelog
|
|
from dzgui.util.format import format_pango
|
|
from dzgui.views.mixins.help_menu_mixin import HelpMenuMixin
|
|
from dzgui.views.mixins.scrollable_mixin import ScrollableMixin
|
|
|
|
import gi
|
|
|
|
gi.require_version("Gtk", "3.0")
|
|
from gi.repository import Gtk # noqa E402
|
|
|
|
logger = logging.getLogger(APP_NAME)
|
|
|
|
if TYPE_CHECKING:
|
|
from dzgui.controllers.mc import Controller
|
|
|
|
|
|
class Changelog(HelpMenuMixin, ScrollableMixin, Gtk.ScrolledWindow): # type: ignore
|
|
def __init__(self, controller: "Controller"):
|
|
super().__init__(propagate_natural_width=False)
|
|
|
|
try:
|
|
changelog = resources.read_text(APP_NAME_LOWER, CHANGELOG_PATH)
|
|
except OSError as e:
|
|
logger.critical(e)
|
|
changelog = missing_changelog
|
|
|
|
self.controller = controller
|
|
self.box = Gtk.Box(
|
|
orientation=Gtk.Orientation.VERTICAL, spacing=5, margin_top=10
|
|
)
|
|
self.add(self.box)
|
|
|
|
expand_all = Gtk.Button(
|
|
label=clog.expand_all, halign=Gtk.Align.START, margin_start=20
|
|
)
|
|
expand_all.connect("clicked", self._on_expand_all_clicked)
|
|
self.box.add(expand_all)
|
|
|
|
self.expanded = False
|
|
self.expanders: list[Gtk.Expander] = []
|
|
self.connect("key-press-event", self._on_keypress)
|
|
self.connect("key-press-event", self._on_esc_keypress)
|
|
|
|
changes = self._parse(changelog)
|
|
self._generate_nodes(changes)
|
|
|
|
self.show_all()
|
|
|
|
def _on_expand_all_clicked(self, button: Gtk.Button) -> None:
|
|
self.expanded = not self.expanded
|
|
for expander in self.expanders:
|
|
# NOTE: simply setting set_expanded() does not trigger activate() signal,
|
|
# so margins are not applied
|
|
if expander.get_expanded() == self.expanded:
|
|
continue
|
|
expander.activate()
|
|
label = clog.collapse_all if self.expanded else clog.expand_all
|
|
button.set_label(label)
|
|
|
|
def grab_content_area(self) -> None:
|
|
self.grab_focus()
|
|
|
|
def _parse_version(self, version: str) -> tuple[str, str]:
|
|
pattern = r"(^##\s\[)(.+)(\] )(.+$)"
|
|
match = re.match(pattern, version)
|
|
if match:
|
|
version = match.group(2)
|
|
date = match.group(4)
|
|
return version, date
|
|
raise ValueError("Changelog parse error")
|
|
|
|
def _generate_nodes(self, releases: list[tuple[str, list[str]]]) -> None:
|
|
for header, changes in releases:
|
|
for ind in (0, -1):
|
|
if changes[ind] == "":
|
|
del changes[ind]
|
|
text = "\n".join(changes)
|
|
formatted = format_pango(text)
|
|
|
|
container = Gtk.Box(valign=Gtk.Align.START, halign=Gtk.Align.START)
|
|
label = Gtk.Label()
|
|
label.set_markup(formatted)
|
|
container.add(label)
|
|
|
|
version, date = self._parse_version(header)
|
|
button_text = f"{version} ({date})"
|
|
button = Gtk.Button(label=button_text, margin_start=5)
|
|
|
|
expander = Gtk.Expander()
|
|
expander.set_label_widget(button)
|
|
expander.add(container)
|
|
expander.connect("activate", self._on_expand, container)
|
|
self.box.add(expander)
|
|
self.expanders.append(expander)
|
|
|
|
def _on_expand(self, expander: Gtk.Box, container: Gtk.Box) -> None:
|
|
"""
|
|
Wayland/some themes do not expect widgets to have a size allocation until expanded.
|
|
Trying to set margins a priori may cause negative allocation,
|
|
as the margins are subtracted from zero. In effect, the widget is not realized
|
|
until it is expanded for the first time.
|
|
"""
|
|
container.set_margin_start(15)
|
|
container.set_margin_top(15)
|
|
container.set_margin_bottom(15)
|
|
|
|
def _parse(self, changelog: str) -> list[tuple[str, list[str]]]:
|
|
|
|
release = ""
|
|
releases: list[tuple[str, list[str]]] = []
|
|
release_notes: list[str] = []
|
|
for line in changelog.splitlines():
|
|
if line.startswith("# "):
|
|
continue
|
|
if line.startswith("## "):
|
|
if release != "":
|
|
releases.append((release, release_notes))
|
|
release_notes = []
|
|
release = ""
|
|
release = line
|
|
continue
|
|
release_notes.append(textwrap.fill(line.rstrip(), width=120))
|
|
return releases
|