mirror of
https://git.hiabuto.net/C3MA/CCMA.git
synced 2026-08-26 23:35:19 +02:00
feat: initialize CCMA member administration
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import tkinter as tk
|
||||
from pathlib import Path
|
||||
from tkinter import ttk
|
||||
from typing import Any
|
||||
|
||||
from ccma import __version__
|
||||
from ccma.ui.icons import IconStore
|
||||
|
||||
CHANGELOG_PATH = Path(__file__).resolve().parent.parent / "assets" / "CHANGELOG.json"
|
||||
|
||||
|
||||
def load_changelog(path: Path = CHANGELOG_PATH) -> list[dict[str, Any]]:
|
||||
try:
|
||||
data = json.loads(path.read_text(encoding="utf-8"))
|
||||
except (OSError, ValueError, TypeError):
|
||||
return []
|
||||
if not isinstance(data, list):
|
||||
return []
|
||||
return [entry for entry in data if isinstance(entry, dict)]
|
||||
|
||||
|
||||
class ChangelogView(ttk.Frame):
|
||||
def __init__(self, master: tk.Misc):
|
||||
super().__init__(master, padding=14)
|
||||
self.icons = IconStore(self)
|
||||
self.entries = load_changelog()
|
||||
self._build_ui()
|
||||
|
||||
def _build_ui(self) -> None:
|
||||
self.columnconfigure(0, weight=1)
|
||||
self.rowconfigure(1, weight=1)
|
||||
header = ttk.Frame(self)
|
||||
header.grid(row=0, column=0, sticky="ew", pady=(0, 14))
|
||||
header.columnconfigure(1, weight=1)
|
||||
history_icon = self.icons.get("history", 28)
|
||||
icon_label = ttk.Label(header, image=history_icon)
|
||||
icon_label.image = history_icon
|
||||
icon_label.grid(row=0, column=0, rowspan=2, padx=(0, 10))
|
||||
ttk.Label(header, text="Änderungsverlauf", style="TabTitle.TLabel").grid(row=0, column=1, sticky="w")
|
||||
ttk.Label(
|
||||
header,
|
||||
text=f"Aktuelle Version {__version__} · {len(self.entries)} Release-Einträge",
|
||||
style="Mono.TLabel",
|
||||
).grid(row=1, column=1, sticky="w", pady=(2, 0))
|
||||
|
||||
container = ttk.Frame(self)
|
||||
container.grid(row=1, column=0, sticky="nsew")
|
||||
container.columnconfigure(0, weight=1)
|
||||
container.rowconfigure(0, weight=1)
|
||||
background = ttk.Style(self).lookup("TFrame", "background") or "#ffffff"
|
||||
self.canvas = tk.Canvas(container, highlightthickness=0, borderwidth=0, background=background)
|
||||
scrollbar = ttk.Scrollbar(container, orient="vertical", command=self.canvas.yview)
|
||||
self.canvas.configure(yscrollcommand=scrollbar.set)
|
||||
self.canvas.grid(row=0, column=0, sticky="nsew")
|
||||
scrollbar.grid(row=0, column=1, sticky="ns")
|
||||
self.content = ttk.Frame(self.canvas)
|
||||
self.content.columnconfigure(0, weight=1)
|
||||
self.content_window = self.canvas.create_window((0, 0), anchor="nw", window=self.content)
|
||||
self.content.bind("<Configure>", self._update_scroll_region)
|
||||
self.canvas.bind("<Configure>", self._resize_content)
|
||||
self._render_entries()
|
||||
self._bind_mousewheel_tree(self.canvas)
|
||||
self._bind_mousewheel_tree(self.content)
|
||||
|
||||
def _render_entries(self) -> None:
|
||||
if not self.entries:
|
||||
ttk.Label(self.content, text="Kein Changelog verfügbar.", style="Muted.TLabel").grid(
|
||||
row=0, column=0, sticky="w", padx=8, pady=8
|
||||
)
|
||||
return
|
||||
for row, entry in enumerate(self.entries):
|
||||
version = str(entry.get("version", "unbekannt"))
|
||||
card = ttk.LabelFrame(self.content, padding=14)
|
||||
card.grid(row=row, column=0, sticky="ew", padx=(0, 8), pady=(0, 12))
|
||||
card.columnconfigure(1, weight=1)
|
||||
ttk.Label(card, text=f"VERSION {version}", style="TimelineHeader.TLabel").grid(
|
||||
row=0, column=0, sticky="w"
|
||||
)
|
||||
if version == __version__:
|
||||
ttk.Label(card, text="CURRENT", style="Mono.TLabel").grid(
|
||||
row=0, column=1, sticky="w", padx=(12, 0)
|
||||
)
|
||||
ttk.Label(card, text=str(entry.get("date", "")), style="Muted.TLabel").grid(
|
||||
row=0, column=2, sticky="e"
|
||||
)
|
||||
changes = entry.get("changes", [])
|
||||
if not isinstance(changes, list):
|
||||
continue
|
||||
for change_row, change in enumerate(changes, start=1):
|
||||
ttk.Label(card, text="•", style="TimelineHeader.TLabel").grid(
|
||||
row=change_row, column=0, sticky="nw", pady=(7, 0)
|
||||
)
|
||||
ttk.Label(
|
||||
card,
|
||||
text=str(change),
|
||||
justify="left",
|
||||
wraplength=650,
|
||||
).grid(row=change_row, column=1, columnspan=2, sticky="w", padx=(8, 0), pady=(7, 0))
|
||||
|
||||
def _update_scroll_region(self, _event: tk.Event | None = None) -> None:
|
||||
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
|
||||
|
||||
def _resize_content(self, event: tk.Event) -> None:
|
||||
self.canvas.itemconfigure(self.content_window, width=event.width)
|
||||
|
||||
def _bind_mousewheel_tree(self, widget: tk.Misc) -> None:
|
||||
widget.bind("<MouseWheel>", self._on_mousewheel, add="+")
|
||||
widget.bind("<Button-4>", self._on_mousewheel, add="+")
|
||||
widget.bind("<Button-5>", self._on_mousewheel, add="+")
|
||||
for child in widget.winfo_children():
|
||||
self._bind_mousewheel_tree(child)
|
||||
|
||||
def _on_mousewheel(self, event: tk.Event) -> None:
|
||||
if getattr(event, "num", None) == 4:
|
||||
delta = -1
|
||||
elif getattr(event, "num", None) == 5:
|
||||
delta = 1
|
||||
else:
|
||||
delta = -1 if getattr(event, "delta", 0) > 0 else 1
|
||||
self.canvas.yview_scroll(delta * 3, "units")
|
||||
Reference in New Issue
Block a user