From cc708c64b299e82ca52143da72bea5626dc87414 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Mon, 9 Nov 2026 23:19:32 +0900 Subject: [PATCH 01/34] docs: add history file --- HISTORY.md | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 HISTORY.md diff --git a/HISTORY.md b/HISTORY.md new file mode 100644 index 0000000..019b55e --- /dev/null +++ b/HISTORY.md @@ -0,0 +1,119 @@ +# History of the project +This file collects notable milestones and events related to DZGUI, mainly around DZGUI as a framework and the history of development. +A chronological summary of features can be found in CHANGELOG.md. + +## 2021 +### 2021-12-03: DayZ adds support for Battleye over Proton +DayZ was among a handful of games with whitelisted anti-cheat support under Valve's emerging WINE-based Proton compatibility layer. +While adding anti-cheat compatibility at a technical level, this change did not actually allow the client to connect to servers running complex modlists. + +Development of the precursor to DZGUI began at around this time. + +### 2021-12-12: initial version of DZTUI is created +DZTUI, a Bash script and terminal user interface, was the precursor to DZGUI. +This was an initial attempt at bootstrapping the Steam client to handle mods correctly when launching DayZ. +Due to user interest, features continued to be added. This early tool is archived under the 'dztui' branch. + +## 2022 +### 2022-05-09: addition of zenity GUI and project name change (DZGUI) +With the introduction of the Steam Deck, more users wanted a graphical user interface +to interact with DZTUI. Being that DZTUI was purely a Bash script, a method of +drawing menus was needed, so the 'zenity' package was used to create prefabricated dialogs. +While this met the minimum requirement to show prompts and selectable options, no rich customization was possible. + +The introduction of zenity as a dependency marked the shift from "DZTUI" to "DZGUI". + +Throughout 2022, the limits of zenity for DZGUI's purposes were tested, particularly in the context of SteamOS. +This led to the discovery of a number of bugs in zenity, GTK, and SteamOS, which were reported up to their respective channels. +While this was improving the SteamOS user experience, it started to become clear that a fully custom UI was needed. + +This barebones version of DZGUI sufficed for some time, with the Bash script and some standalone Python modules doing the heavy lifting, +and this period served to surface a number of bugs and interesting interactions with the Steam client. + +## 2023 +### 2023-11-23: Python 3.10 dependency +Prior to this time, DZGUI open-endedly allowed you to use any Python version for its modules, without any significant handling for +the idiosyncrasies of different versions. A minimum requirement of Python 3.10 was thus instituted to normalize expected behavior. + +Throughout 2023, various features related to server browsing were added, such as filters and more granular search parameters. + +There were still a lot of unknowns at this time, so things were being resolved through trial and error. + +## 2024 +### 2024-01-31: DZGUI 5 API introduces GTK UI +Using zenity as a simple GUI had exposed some bugs and limitations in that package, particularly when trying to push huge lists of server records +into tables and sorting them in a performant manner. + +Around this time it became clear that a more robust UI framework was needed. While keeping the original Bash script and Python modules for +background processing, PyGObject (GTK) was added to the project in order to ship a custom UI that could be designed from the ground up. + +However, it should be noted that at this point, the DZGUI logic itself was still a (increasingly large) Bash script to which a GTK UI had been stitched on. +These were totally separate components, with UI actions on the GTK side triggering calls to Bash functions in an overly complex helper file. +The UI was basically a thin client around the old Bash logic, but this was still a far cry from an integrated application with proper signals and dedicated components. + +Effectively, DZGUI 5 through 6 were a stepping stone that set the groundwork to go from DZGUI 4 (pure Bash script) to +DZGUI 7 (standalone Python application). Today, little of DZGUI 5's original logic remains outside of the basic UI grid. + +### 2024-10-15: Frostline DLC released, widespread parsing problems +DayZ's first DLC introduced a new format for server responses that packed extra metadata into the A2S_RULES format. +Upon its release, all third-party DayZ launchers and server listing web sites were unable to parse the mod information being returned from Frostline servers. + +For a period of two weeks, this remained unsolved and various attempts were made to understand the updated specification. + +### 2024-10-30: DZGUI hotfix adds support for Frostline servers +DZGUI was the first third-party tool to fix parsing for Frostline servers and restore functionality. +This was a small milestone, because it meant DZGUI was evolving from a "dumb" launcher to a featureful tool +touching upon and improving all aspects of the end-to-end DayZ experience. + +Particularly around this time, the sheer diversity of needs of modders, server operators, and players started to become clear. +Users had highly specific and diverse use cases for setting up their game environment, and requested a number of features to support that on +the DZGUI side. + +## 2025 +### 2025-01-17: DZGUI 6 API +With DZGUI 6, the difficult decision to enforce a Python 3.13 dependency was made. +While the previous dependency was Python 3.10+, it was deemed better to develop against and target identical versions. + +However, due to the vagaries of managing Python environments on end-user systems, this change added unnecessary complexity and frustration. +Users had to properly set up virtual environments, install the relevant dependencies, and create a wrapper script to run this version, whereas +previous versions had essentially been point and click. + +### 2025-10-23: DZGUI 7 API and the "Ship of Theseus" +It had become clear that the situation above was untenable and the legacy Bash script was unmaintainable given the project's enlarged scope. +The lack of a robust testing or typehinting framework made it difficult to diagnose rare bugs. + +Shipping a standalone, turnkey application with zero or minimal dependencies was the most urgent priority. +DZGUI was still carrying around small bits and pieces of the old Bash script logic, so the decision was made to +refactor the application into a pure Python + GTK tool. This would effectively be a total rewrite of all logic, +only preserving the most minimal skeleton of the original UI layout as a reference. + +Over the course of a year, a wholesale "Ship of Theseus" rewrite took place. +Nearly every component of DZGUI 6, both API and UI side, was taken apart and replaced. + +A soft alpha and beta phase were used to iterate on this concept further. DZGUI 7 was increasingly recommended to users +as the preferred choice. This allowed for keeping the established userbase on the (relatively) stable DZGUI 6 spec, while building up +the new spec and making it available to more users as features were elaborated. + +Through late 2025, several LLM-generated applications that recycled significant parts of DZGUI's code without the applicable GPL license were found. +The maintainers of those projects were advised to excise the offending segments or apply the necessary GPL licenses. + +## 2026 +### 2026-05-17: Switch to standalone releases +The DZGUI 7 beta starts shipping standalone glibc builds with an integrated setup wizard and installer. + +# TODO: remarks on DZGUI 6 stable vs testing branch logic and "over the air" updates vs standalone builds +# TODO: note archived branch names + +### 2026-10-27: 500 tickets closed +The milestone of 500 tickets/PRs being closed was reached. + +2026 was by far DZGUI's most intensive year by commits, perhaps suggesting the scope of the rewrite: + +- 2021: 16 +- 2022: 434 +- 2023: 322 +- 2024: 347 +- 2025: 426 +- 2026: 1300 + +DZGUI 7 is scheduled to release as a stable version within the year. From a42c9d4389e918821fd33e39da2bc9f79c1f1ab0 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Mon, 9 Nov 2026 23:28:50 +0900 Subject: [PATCH 02/34] drop: abstracted constructor params --- dzgui/const/constants.py | 7 ------- dzgui/views/components/buttonbox.py | 4 ++-- dzgui/views/components/filter_panel.py | 13 ++++++------- dzgui/views/components/mod_panel.py | 7 +++---- dzgui/views/components/right_panel.py | 10 +++------- dzgui/views/dialogs/boot.py | 5 ++--- dzgui/views/dialogs/generic.py | 13 ++++++------- dzgui/views/dialogs/link_dialog.py | 3 +-- dzgui/views/pages/options.py | 7 ++----- 9 files changed, 25 insertions(+), 44 deletions(-) diff --git a/dzgui/const/constants.py b/dzgui/const/constants.py index aba522f..65442be 100644 --- a/dzgui/const/constants.py +++ b/dzgui/const/constants.py @@ -61,13 +61,6 @@ NIGHT_ICON = "weather-clear-night-symbolic" SEPARATOR = "SEPARATOR" -# TODO: drop -NO_PADDING = 0 -NO_EXPAND = False -NO_FILL = False -EXPAND = True -FILL = True - SCROLL_INCREMENT = 50 WINDOW_DEFAULT_X = 1400 WINDOW_DEFAULT_Y = 800 diff --git a/dzgui/views/components/buttonbox.py b/dzgui/views/components/buttonbox.py index 496f237..5fd1956 100644 --- a/dzgui/views/components/buttonbox.py +++ b/dzgui/views/components/buttonbox.py @@ -1,7 +1,7 @@ import logging from typing import TYPE_CHECKING -from dzgui.const.constants import APP_NAME, NO_EXPAND, NO_FILL, NO_PADDING +from dzgui.const.constants import APP_NAME from dzgui.const.enum import ButtonType import gi @@ -63,7 +63,7 @@ class ButtonBox(Gtk.Box): self.buttons.append(button) button.connect("clicked", self._on_selection_button_clicked) - self.pack_start(button, NO_EXPAND, NO_FILL, NO_PADDING) + self.pack_start(button, expand=False, fill=False, padding=0) def _focus_first_button(self, emitter: "Emitter") -> None: self.buttons[0].grab_focus() diff --git a/dzgui/views/components/filter_panel.py b/dzgui/views/components/filter_panel.py index 75c51e9..5d80c9d 100644 --- a/dzgui/views/components/filter_panel.py +++ b/dzgui/views/components/filter_panel.py @@ -3,10 +3,6 @@ from typing import Literal, TYPE_CHECKING from dzgui.const.constants import ( APP_NAME, - EXPAND, - NO_EXPAND, - NO_FILL, - NO_PADDING, SEARCH_ICON, ) from dzgui.util.format import format_exception @@ -42,7 +38,10 @@ class ButtonGrid(Gtk.Grid): self.checks: list[Gtk.CheckButton] = [] flowbox = Gtk.FlowBox( - halign=Gtk.Align.CENTER, min_children_per_line=3, max_children_per_line=3, selection_mode=Gtk.SelectionMode.NONE + halign=Gtk.Align.CENTER, + min_children_per_line=3, + max_children_per_line=3, + selection_mode=Gtk.SelectionMode.NONE, ) for check in defaults.keys(): @@ -201,7 +200,7 @@ class FilterPanel(Gtk.Box): self.maps_entry.connect("key-press-event", self._on_map_entry_keypress) self.maps_entry.connect("activate", self._on_maps_activated) - self.maps_combo.pack_start(renderer_text, EXPAND) + self.maps_combo.pack_start(renderer_text, expand=True) self.maps_combo.connect("changed", self._on_map_changed) self.maps_combo.connect("key-press-event", self._on_combo_keypress) @@ -211,7 +210,7 @@ class FilterPanel(Gtk.Box): self.maps_combo, self.button_grid, ): - self.pack_start(el, NO_EXPAND, NO_FILL, NO_PADDING) + self.pack_start(el, expand=False, fill=False, padding=0) def _on_maps_activated(self, entry: Gtk.Entry) -> None: text = entry.get_text() diff --git a/dzgui/views/components/mod_panel.py b/dzgui/views/components/mod_panel.py index 7c913c4..380f7ea 100644 --- a/dzgui/views/components/mod_panel.py +++ b/dzgui/views/components/mod_panel.py @@ -1,7 +1,6 @@ from typing import Self, TYPE_CHECKING from dzgui.const.enum import ModButton -from dzgui.const.constants import NO_EXPAND, FILL, NO_PADDING from dzgui.util.strings import mod_panel from dzgui.views.components.labels import BoldLabel @@ -55,7 +54,7 @@ class ModSelectionPanel(Gtk.Box): for button in buttons: b = EnumeratedModButton(button) b.connect("clicked", self._on_button_clicked) - self.main_panel.pack_start(b, NO_EXPAND, FILL, NO_PADDING) + self.main_panel.pack_start(b, expand=False, fill=True, padding=0) self.stale_panel = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) self.highlight_stale_button = ModPanelButton(ModButton.HIGHLIGHT_STALE) @@ -67,11 +66,11 @@ class ModSelectionPanel(Gtk.Box): self.unhighlight_stale_button, self.select_stale_button, ): - self.stale_panel.pack_start(b, NO_EXPAND, FILL, NO_PADDING) + self.stale_panel.pack_start(b, expand=False, fill=True, padding=0) b.connect("clicked", self._on_button_clicked) for el in header, self.main_panel, self.stale_panel: - self.pack_start(el, NO_EXPAND, FILL, NO_PADDING) + self.pack_start(el, expand=False, fill=True, padding=0) self.connect("map", self._reinit_button) diff --git a/dzgui/views/components/right_panel.py b/dzgui/views/components/right_panel.py index f38def4..a5efa3c 100644 --- a/dzgui/views/components/right_panel.py +++ b/dzgui/views/components/right_panel.py @@ -6,10 +6,6 @@ from typing import Literal, TYPE_CHECKING from dzgui.const.constants import ( APP_NAME, - NO_EXPAND, - NO_FILL, - FILL, - NO_PADDING, ) from dzgui.const.enum import ServerTab from dzgui.managers.update import UpdateManager @@ -73,9 +69,9 @@ class RightPanel(Gtk.Box): eb.connect("button-press-event", self._on_version_clicked) for el in self.button_vbox, self.keys, self.filters_vbox, self.refresh_button: - self.pack_start(el, NO_EXPAND, FILL, NO_PADDING) + self.pack_start(el, expand=False, fill=True, padding=0) - self.pack_start(self.sel_panel, NO_EXPAND, NO_FILL, NO_PADDING) + self.pack_start(self.sel_panel, expand=False, fill=True, padding=0) self.update_button = IconTextButton( "dialog-information-symbolic", label="Updates available" @@ -96,7 +92,7 @@ class RightPanel(Gtk.Box): "clicked", self._on_update_button_clicked, exe_path, update ) self.gutter_box.add(eb) - self.pack_start(self.gutter_box, NO_EXPAND, FILL, NO_PADDING) + self.pack_start(self.gutter_box, expand=False, fill=True, padding=0) def _on_update_button_clicked( self, button: Gtk.Button, exe_path: str, url: str diff --git a/dzgui/views/dialogs/boot.py b/dzgui/views/dialogs/boot.py index d4aa557..471e2ce 100644 --- a/dzgui/views/dialogs/boot.py +++ b/dzgui/views/dialogs/boot.py @@ -14,7 +14,6 @@ from dzgui.const.constants import HEX_GREEN, HEX_RED from dzgui.init.coords import get_local_coords from dzgui.init.dayz import is_dayz_installed from dzgui.init.update import check_updates -from dzgui.const.constants import EXPAND, FILL from dzgui.managers.threading import call_on_thread, StoredFunc, ThreadingManager from dzgui.strings import preboot, dialogs from dzgui.util.format import format_exception @@ -118,8 +117,8 @@ class BootDialog(ColorAwareApp, Gtk.Dialog): # type: ignore self.error_box.add(self.button_hbox) self.content = self.get_content_area() - self.content.pack_start(self.scrollable_tree, EXPAND, FILL, 0) - self.content.pack_start(self.error_box, EXPAND, FILL, 0) + self.content.pack_start(self.scrollable_tree, expand=True, fill=True, padding=0) + self.content.pack_start(self.error_box, expand=True, fill=True, padding=0) self.show_all() self.error_box.hide() diff --git a/dzgui/views/dialogs/generic.py b/dzgui/views/dialogs/generic.py index cff1e2a..dc57daf 100644 --- a/dzgui/views/dialogs/generic.py +++ b/dzgui/views/dialogs/generic.py @@ -1,6 +1,5 @@ from typing import Literal, Self, TYPE_CHECKING -from dzgui.const.constants import NO_EXPAND, NO_FILL, NO_PADDING, EXPAND, FILL from dzgui.strings import dialogs from dzgui.util import strings from dzgui.views.components.buttons import ClipboardButton @@ -46,7 +45,7 @@ class GenericDialog(Gtk.MessageDialog): def pack(self, widget: Gtk.Widget) -> None: content = self.get_content_area() - content.pack_start(widget, EXPAND, FILL, NO_PADDING) + content.pack_start(widget, expand=True, fill=True, padding=0) class ConfirmationDialog(GenericDialog): @@ -121,13 +120,13 @@ class WaitDialog(GenericDialog): spinner_box.add(spinner) if self.jobs > 1: - content.pack_start(self.prog, NO_EXPAND, NO_FILL, 0) + content.pack_start(self.prog, expand=False, fill=False, padding=0) else: - content.pack_start(spinner_box, NO_EXPAND, NO_FILL, 0) + content.pack_start(spinner_box, expand=False, fill=False, padding=0) spinner.start() if show_cancel is True: - content.pack_start(self.cancel, NO_EXPAND, NO_FILL, 0) + content.pack_start(self.cancel, expand=False, fill=False, padding=0) def update_text(self, msg: str) -> None: self.format_secondary_text(msg) @@ -272,7 +271,7 @@ class ExceptionDialog(TextBufferDialog): bottom_margin=10, ) textview.set_buffer(Gtk.TextBuffer(text=trace)) - box.pack_start(textview, EXPAND, FILL, 0) + box.pack_start(textview, expand=True, fill=True, padding=0) self.error_details = Gtk.ScrolledWindow( overlay_scrolling=False, @@ -296,7 +295,7 @@ class ExceptionDialog(TextBufferDialog): ) details_textview.set_buffer(self.details_buffer) - details_box.pack_start(details_textview, EXPAND, FILL, 10) + details_box.pack_start(details_textview, expand=True, fill=True, padding=10) self.error_details.add(details_box) self.error_notebook = Gtk.Notebook(show_tabs=False, margin_bottom=15) diff --git a/dzgui/views/dialogs/link_dialog.py b/dzgui/views/dialogs/link_dialog.py index 294765c..a2fbda2 100644 --- a/dzgui/views/dialogs/link_dialog.py +++ b/dzgui/views/dialogs/link_dialog.py @@ -1,7 +1,6 @@ import textwrap from typing import Self, TYPE_CHECKING -from dzgui.const.constants import NO_EXPAND, NO_FILL from dzgui.util.strings import notice from dzgui.views.dialogs.generic import GenericDialog from dzgui.views.components.buttons import SteamTextButton @@ -42,7 +41,7 @@ class WorkshopLinkDialog(GenericDialog): button.set_margin_start(60) button.set_margin_end(60) button.connect("clicked", self._on_button_clicked, uid) - self.dialogBox.pack_end(button, NO_EXPAND, NO_FILL, 0) + self.dialogBox.pack_end(button, expand=False, fill=False, padding=0) self.show_all() self.connect("response", self._on_dialog_response) diff --git a/dzgui/views/pages/options.py b/dzgui/views/pages/options.py index b5633ca..2fbf5b9 100644 --- a/dzgui/views/pages/options.py +++ b/dzgui/views/pages/options.py @@ -13,9 +13,6 @@ from dzgui.const.constants import ( FLATPAK_RUN_CMD, FLATPAK_SANDBOX, FLATPAK_STEAM_PATH, - NO_EXPAND, - NO_FILL, - NO_PADDING, VIEW_CONCEAL, VIEW_REVEAL, ) @@ -234,8 +231,8 @@ class ToggleField(Gtk.Box): self.radio1 = Gtk.RadioButton.new_with_label(None, first_option) self.radio2 = Gtk.RadioButton.new_from_widget(self.radio1) self.radio2.set_label(second_option) - self.pack_start(self.radio1, NO_EXPAND, NO_FILL, NO_PADDING) - self.pack_start(self.radio2, NO_EXPAND, NO_FILL, NO_PADDING) + self.pack_start(self.radio1, expand=False, fill=False, padding=0) + self.pack_start(self.radio2, expand=False, fill=False, padding=0) def set_suboption_active(self, state: bool) -> None: # NOTE: defer connection of signal until after state is set From 72e3d95808fe10b65e3f3e643426c3dccc87e3b7 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Mon, 9 Nov 2026 23:48:10 +0900 Subject: [PATCH 03/34] change: normalize icon names --- dzgui/const/constants.py | 22 ---------------- dzgui/const/icons.py | 20 +++++++++++++++ dzgui/views/components/buttons.py | 34 ++++++++++++------------- dzgui/views/components/connect_panel.py | 9 ++++--- dzgui/views/components/entry.py | 8 +++--- dzgui/views/components/eventbox.py | 4 +-- dzgui/views/components/filter_panel.py | 4 +-- dzgui/views/components/misc.py | 5 ++-- dzgui/views/dialogs/calc.py | 6 ++--- dzgui/views/dialogs/servers.py | 4 +-- dzgui/views/pages/offline.py | 10 +++----- dzgui/views/pages/options.py | 9 +++---- dzgui/views/pages/preconnect.py | 10 ++++---- 13 files changed, 71 insertions(+), 74 deletions(-) create mode 100644 dzgui/const/icons.py diff --git a/dzgui/const/constants.py b/dzgui/const/constants.py index aba522f..18affdb 100644 --- a/dzgui/const/constants.py +++ b/dzgui/const/constants.py @@ -37,28 +37,6 @@ HEX_GREEN = "#32CD32" HEX_RED = "#FF0000" HEX_ORANGE = "#FFAC1C" -# TODO: normalize icon names -CALCULATOR = "accessories-calculator-symbolic" -CARET_DOWN = "go-down-symbolic" -CARET_UP = "go-up-symbolic" -CLIPBOARD = "edit-copy-symbolic" -CLOSE = "window-close-symbolic" -FOLDER = "folder-symbolic" -EDIT_DELETE = "edit-delete-symbolic" -ERROR = "dialog-error-symbolic" -HELP_BUBBLE = "help-about-symbolic" -INPUT_KEYBOARD = "input-keyboard-symbolic" -LIST_ADD = "list-add-symbolic" -REFRESH_ICON = "view-refresh-symbolic" -SEARCH_ICON = "system-search-symbolic" -STEAM_ICON = "steam-tray-symbolic" -VIEW_CONCEAL = "view-conceal-symbolic" -VIEW_REVEAL = "view-reveal-symbolic" -WARNING = "dialog-warning-symbolic" -WEB_BROWSER = "web-browser-symbolic" -DAY_ICON = "weather-clear-symbolic" -NIGHT_ICON = "weather-clear-night-symbolic" - SEPARATOR = "SEPARATOR" # TODO: drop diff --git a/dzgui/const/icons.py b/dzgui/const/icons.py new file mode 100644 index 0000000..176e73e --- /dev/null +++ b/dzgui/const/icons.py @@ -0,0 +1,20 @@ +ICON_CALCULATOR = "accessories-calculator-symbolic" +ICON_CARET_DOWN = "go-down-symbolic" +ICON_CARET_UP = "go-up-symbolic" +ICON_CLIPBOARD = "edit-copy-symbolic" +ICON_CLOSE = "window-close-symbolic" +ICON_DAY = "weather-clear-symbolic" +ICON_EDIT_DELETE = "edit-delete-symbolic" +ICON_ERROR = "dialog-error-symbolic" +ICON_FOLDER = "folder-symbolic" +ICON_HELP_BUBBLE = "help-about-symbolic" +ICON_INPUT_KEYBOARD = "input-keyboard-symbolic" +ICON_LIST_ADD = "list-add-symbolic" +ICON_NIGHT = "weather-clear-night-symbolic" +ICON_REFRESH = "view-refresh-symbolic" +ICON_SEARCH = "system-search-symbolic" +ICON_STEAM = "steam-tray-symbolic" +ICON_VIEW_CONCEAL = "view-conceal-symbolic" +ICON_VIEW_REVEAL = "view-reveal-symbolic" +ICON_WARNING = "dialog-warning-symbolic" +ICON_WEB_BROWSER = "web-browser-symbolic" diff --git a/dzgui/views/components/buttons.py b/dzgui/views/components/buttons.py index bb3d45d..2135892 100644 --- a/dzgui/views/components/buttons.py +++ b/dzgui/views/components/buttons.py @@ -7,15 +7,15 @@ from dzgui.util.strings import ( alert_button_tooltip, atomic_buttons, ) -from dzgui.const.constants import ( - CLIPBOARD, - CLOSE, - INPUT_KEYBOARD, - LIST_ADD, - REFRESH_ICON, - STEAM_ICON, - WARNING, - WEB_BROWSER, +from dzgui.const.icons import ( + ICON_CLIPBOARD, + ICON_CLOSE, + ICON_INPUT_KEYBOARD, + ICON_LIST_ADD, + ICON_REFRESH, + ICON_STEAM, + ICON_WARNING, + ICON_WEB_BROWSER, ) from dzgui.strings import connect_panel @@ -102,7 +102,7 @@ class LargeIconTextButton(IconButton): class ClipboardButton(IconTextButton): def __init__(self, controller: Union["Controller", None], func: Callable) -> None: - super().__init__(CLIPBOARD, atomic_buttons.copy) + super().__init__(ICON_CLIPBOARD, atomic_buttons.copy) self.controller = controller self.connect("clicked", self._on_button_clicked, func) @@ -131,7 +131,7 @@ class CopyIpButton(ClipboardButton): class CloseButton(IconTextButton): def __init__(self, label: str) -> None: - super().__init__(icon=CLOSE, label=label) + super().__init__(icon=ICON_CLOSE, label=label) # TODO: strings self.set_tooltip_text("Unset this server as favorite") @@ -139,14 +139,14 @@ class CloseButton(IconTextButton): class WebButton(IconTextButton): def __init__(self, label: str) -> None: - super().__init__(icon=WEB_BROWSER, label=label) + super().__init__(icon=ICON_WEB_BROWSER, label=label) pass class RefreshButton(IconTextButton): def __init__(self, controller: "Controller") -> None: super().__init__( - icon=REFRESH_ICON, + icon=ICON_REFRESH, label=atomic_buttons.refresh, ) @@ -211,7 +211,7 @@ class RefreshButton(IconTextButton): class KeysButton(IconTextButton): def __init__(self, controller: "Controller") -> None: - super().__init__(icon=INPUT_KEYBOARD, label=atomic_buttons.keys) + super().__init__(icon=ICON_INPUT_KEYBOARD, label=atomic_buttons.keys) self.controller = controller self.set_margin_top(10) @@ -228,7 +228,7 @@ class KeysButton(IconTextButton): class SteamConnectButton(LargeIconTextButton): def __init__(self) -> None: - super().__init__(icon=STEAM_ICON, label=connect_panel.connect_button) + super().__init__(icon=ICON_STEAM, label=connect_panel.connect_button) self.set_tooltip_text(connect_panel.connect_tooltip) @@ -248,7 +248,7 @@ class SteamWorkshopButton(SteamTextButton): class AddButton(IconTextButton): def __init__(self) -> None: - super().__init__(icon=LIST_ADD, label=connect_panel.add_button) + super().__init__(icon=ICON_LIST_ADD, label=connect_panel.add_button) self.set_tooltip_text(connect_panel.add_tooltip) @@ -267,7 +267,7 @@ class LoggerAlertsButton(IconTextButton): separator = "," concat = warnings_text + separator + errors_text super().__init__( - icon=WARNING, + icon=ICON_WARNING, label=f"Loaded with{concat}", ) self.set_halign(Gtk.Align.END) diff --git a/dzgui/views/components/connect_panel.py b/dzgui/views/components/connect_panel.py index 17d0a71..2884baa 100644 --- a/dzgui/views/components/connect_panel.py +++ b/dzgui/views/components/connect_panel.py @@ -1,6 +1,7 @@ from typing import Self, TYPE_CHECKING -from dzgui.const.constants import CARET_DOWN, CARET_UP, UDP_PORT +from dzgui.const.icons import ICON_CARET_DOWN, ICON_CARET_UP +from dzgui.const.constants import UDP_PORT from dzgui.model.servers import ServerModelManager from dzgui.strings import connect_panel from dzgui.util.keys import is_ctrl_mask @@ -30,7 +31,7 @@ ROWS = 1 class DockButton(IconButton): def __init__(self, child: Gtk.Widget) -> None: - super().__init__(CARET_DOWN) + super().__init__(ICON_CARET_DOWN) self.expanded = True self.child = child @@ -43,12 +44,12 @@ class DockButton(IconButton): self.set_tooltip_text(self.collapse_text) def collapse(self) -> None: - self.set_image(Icon(CARET_UP)) + self.set_image(Icon(ICON_CARET_UP)) self.child.hide() self.set_tooltip_text(self.expand_text) def expand(self) -> None: - self.set_image(Icon(CARET_DOWN)) + self.set_image(Icon(ICON_CARET_DOWN)) self.child.show() self.set_tooltip_text(self.collapse_text) diff --git a/dzgui/views/components/entry.py b/dzgui/views/components/entry.py index e1b3e7e..1f7fe0f 100644 --- a/dzgui/views/components/entry.py +++ b/dzgui/views/components/entry.py @@ -1,7 +1,7 @@ from typing import Any, Callable, TYPE_CHECKING from dzgui.api.servers import validate_ip -from dzgui.const.constants import VIEW_CONCEAL, VIEW_REVEAL +from dzgui.const.icons import ICON_VIEW_CONCEAL, ICON_VIEW_REVEAL from dzgui.util.css import add_class, remove_class from dzgui.strings import connect_panel from dzgui.strings.errors import api_popover @@ -147,7 +147,7 @@ class APIEntry(Gtk.Box): self.entry = Gtk.Entry( width_chars=60, hexpand=True, placeholder_text="Enter API key" ) - self.entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, VIEW_REVEAL) + self.entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, ICON_VIEW_REVEAL) self.entry.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, True) self.entry.set_visibility(False) self.entry.connect("changed", self._on_text_changed) @@ -206,9 +206,9 @@ class APIEntry(Gtk.Box): ) -> None: visible = widget.get_visibility() if visible: - icon, state = VIEW_REVEAL, False + icon, state = ICON_VIEW_REVEAL, False else: - icon, state = VIEW_CONCEAL, True + icon, state = ICON_VIEW_CONCEAL, True widget.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) widget.set_visibility(state) diff --git a/dzgui/views/components/eventbox.py b/dzgui/views/components/eventbox.py index 5e06127..5ef530d 100644 --- a/dzgui/views/components/eventbox.py +++ b/dzgui/views/components/eventbox.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING from dzgui.const.enum import NotebookPage -from dzgui.const.constants import HELP_BUBBLE +from dzgui.const.icons import ICON_HELP_BUBBLE import gi @@ -20,7 +20,7 @@ class InfoEventBox(Gtk.EventBox): self.text = text self.icon = Gtk.Image.new_from_icon_name( - HELP_BUBBLE, Gtk.IconSize.LARGE_TOOLBAR + ICON_HELP_BUBBLE, Gtk.IconSize.LARGE_TOOLBAR ) self.icon.set_opacity(0.8) box = Gtk.Box() diff --git a/dzgui/views/components/filter_panel.py b/dzgui/views/components/filter_panel.py index 75c51e9..9eae88e 100644 --- a/dzgui/views/components/filter_panel.py +++ b/dzgui/views/components/filter_panel.py @@ -7,8 +7,8 @@ from dzgui.const.constants import ( NO_EXPAND, NO_FILL, NO_PADDING, - SEARCH_ICON, ) +from dzgui.const.icons import ICON_SEARCH from dzgui.util.format import format_exception from dzgui.util.strings import all_maps from dzgui.views.components.maps_combo import MapsCombo @@ -109,7 +109,7 @@ class KeywordEntry(Gtk.Entry): self.connect("key-press-event", self._on_keypress) self.connect("icon-release", lambda *args: self.activate()) - self.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, SEARCH_ICON) + self.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, ICON_SEARCH) self.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, True) self.emitter.connect("load_maps", self._on_maps_loaded) diff --git a/dzgui/views/components/misc.py b/dzgui/views/components/misc.py index cdc3f49..80e694e 100644 --- a/dzgui/views/components/misc.py +++ b/dzgui/views/components/misc.py @@ -1,7 +1,8 @@ from typing import Self from warnings import deprecated -from dzgui.const.constants import ERROR, FLATPAK_RUN_CMD, FLATPAK_SANDBOX, STEAM_CMD +from dzgui.const.constants import FLATPAK_RUN_CMD, FLATPAK_SANDBOX, STEAM_CMD +from dzgui.const.icons import ICON_ERROR from dzgui.init.prereqs import has_steam_client, has_flatpak_client from dzgui.strings import options from dzgui.views.components.box import HBox @@ -73,7 +74,7 @@ class ErrorPopover(Gtk.Popover): self.hbox = HBox() self.label = Gtk.Label(label="", margin_start=10, margin_end=10) - error_icon = Icon(ERROR, margin_start=10) + error_icon = Icon(ICON_ERROR, margin_start=10) self.hbox.extend([error_icon, self.label]) self.add(self.hbox) self.show_all() diff --git a/dzgui/views/dialogs/calc.py b/dzgui/views/dialogs/calc.py index 979cb0a..0b6501e 100644 --- a/dzgui/views/dialogs/calc.py +++ b/dzgui/views/dialogs/calc.py @@ -3,7 +3,7 @@ from dataclasses import dataclass from datetime import datetime, time, timedelta from typing import Literal -from dzgui.const.constants import DAY_ICON, NIGHT_ICON +from dzgui.const.icons import ICON_DAY, ICON_NIGHT from dzgui.strings import calc from dzgui.views.components.icon import Icon from dzgui.views.components.labels import BoldLabel @@ -385,7 +385,7 @@ class TimePickerFrame(Gtk.Frame): h1 = BoldLabel(calc.heading_server_time) h2 = BoldLabel(calc.heading_target_time) - self.icon_indicator = Icon(DAY_ICON) + self.icon_indicator = Icon(ICON_DAY) self.update_server_icon() server_time_box = HBox(10) @@ -411,7 +411,7 @@ class TimePickerFrame(Gtk.Frame): self.add(flowbox) def update_server_icon(self) -> None: - icon = DAY_ICON if self.server_time.is_day() else NIGHT_ICON + icon = ICON_DAY if self.server_time.is_day() else ICON_NIGHT self.icon_indicator.set_icon_name(icon) def _on_server_clock_increment(self, emitter: Emitter, time_now: datetime) -> None: diff --git a/dzgui/views/dialogs/servers.py b/dzgui/views/dialogs/servers.py index 6166aee..b58d352 100644 --- a/dzgui/views/dialogs/servers.py +++ b/dzgui/views/dialogs/servers.py @@ -2,7 +2,7 @@ import gi import html from typing import Self, TYPE_CHECKING -from dzgui.const.constants import CALCULATOR +from dzgui.const.icons import ICON_CALCULATOR from dzgui.const.enum import ContextMenuGroup from dzgui.model.model_factory import ModelFactory from dzgui.util import css @@ -110,7 +110,7 @@ class ServerDetailsDialog(ServerDialog): text = format_hyperlinks(text) self.description.set_markup(html.escape(text)) - calc_button = IconTextButton(CALCULATOR, dialogs.toggle_calc) + calc_button = IconTextButton(ICON_CALCULATOR, dialogs.toggle_calc) calc_button.connect("clicked", self._on_calc_pressed) calc_button.set_halign(Gtk.Align.END) calc_button.set_valign(Gtk.Align.START) diff --git a/dzgui/views/pages/offline.py b/dzgui/views/pages/offline.py index b0200b0..ceb5686 100644 --- a/dzgui/views/pages/offline.py +++ b/dzgui/views/pages/offline.py @@ -3,14 +3,12 @@ from enum import Enum from typing import Self, TYPE_CHECKING, Union from dzgui.util import css +from dzgui.const.icons import ICON_EDIT_DELETE, ICON_FOLDER, ICON_WARNING from dzgui.const.constants import ( APPID_DAYZ, APPID_DAYZ_EXP, APPNAME_DAYZ, APPNAME_DAYZ_EXP_HUMAN, - EDIT_DELETE, - FOLDER, - WARNING, ) from dzgui.const.enum import ContextMenuGroup, NotebookPage from dzgui.managers.offline import OfflineManager @@ -87,7 +85,7 @@ class FolderHBox(HBox): self.emitter = controller.get_emitter() self.eb = InfoEventBox(eb_text, controller) - self.button = IconTextButton(FOLDER, btn_label, Gtk.PositionType.LEFT) + self.button = IconTextButton(ICON_FOLDER, btn_label, Gtk.PositionType.LEFT) self.button.set_halign(Gtk.Align.START) self.button.set_image_position(Gtk.PositionType.LEFT) @@ -99,7 +97,7 @@ class FolderHBox(HBox): self.label = Gtk.Label() self.scrolled_label.add(self.label) - self.unset_button = IconTextButton(EDIT_DELETE, offline.unset_button) + self.unset_button = IconTextButton(ICON_EDIT_DELETE, offline.unset_button) self.unset_button.connect("clicked", self._on_unset_clicked) self.spinner = Gtk.Spinner() @@ -195,7 +193,7 @@ class ModFrame(HeadingFrame): self.tree_vbox.extend([self.scrolled, self.status]) self.no_mods = HBox(spacing=10) - no_mods_icon = Icon(WARNING, margin_start=10) + no_mods_icon = Icon(ICON_WARNING, margin_start=10) no_mods_label = Gtk.Label(label=offline.no_local_mods, halign=Gtk.Align.START) self.no_mods.extend([no_mods_icon, no_mods_label]) diff --git a/dzgui/views/pages/options.py b/dzgui/views/pages/options.py index b5633ca..e4d5f18 100644 --- a/dzgui/views/pages/options.py +++ b/dzgui/views/pages/options.py @@ -16,9 +16,8 @@ from dzgui.const.constants import ( NO_EXPAND, NO_FILL, NO_PADDING, - VIEW_CONCEAL, - VIEW_REVEAL, ) +from dzgui.const.icons import ICON_VIEW_CONCEAL, ICON_VIEW_REVEAL from dzgui.const.endpoints import STEAM_API_SETUP from dzgui.const.enum import Preferences, ServerTab from dzgui.strings import options @@ -67,7 +66,7 @@ class SubmitField(Gtk.Box): # TODO: audit for all applicable callbacks if private: self.entry.set_icon_from_icon_name( - Gtk.EntryIconPosition.SECONDARY, VIEW_REVEAL + Gtk.EntryIconPosition.SECONDARY, ICON_VIEW_REVEAL ) self.entry.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, True) self.entry.connect("icon-release", self._on_icon_release) @@ -131,9 +130,9 @@ class SubmitField(Gtk.Box): ) -> None: visible = widget.get_visibility() if visible: - icon, state = VIEW_REVEAL, False + icon, state = ICON_VIEW_REVEAL, False else: - icon, state = VIEW_CONCEAL, True + icon, state = ICON_VIEW_CONCEAL, True widget.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) widget.set_visibility(state) diff --git a/dzgui/views/pages/preconnect.py b/dzgui/views/pages/preconnect.py index b737ff0..a094fc7 100644 --- a/dzgui/views/pages/preconnect.py +++ b/dzgui/views/pages/preconnect.py @@ -1,9 +1,9 @@ from dataclasses import dataclass from typing import Self, Sequence, TYPE_CHECKING -from dzgui.const.constants import ( - ERROR, - WARNING, +from dzgui.const.icons import ( + ICON_ERROR, + ICON_WARNING, ) from dzgui.const.enum import NotebookPage from dzgui.util.css import add_class @@ -166,7 +166,7 @@ class PreConnectionAssistant(Gtk.Box): # TODO: abstract into components self.warning_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) - self.warning_tree = MaskedTree(WARNING) + self.warning_tree = MaskedTree(ICON_WARNING) self.warning_placeholder = Placeholder(preconnect.placeholder_no_warnings) self.warning_box.add(self.warning_tree) self.warning_box.add(self.warning_placeholder) @@ -175,7 +175,7 @@ class PreConnectionAssistant(Gtk.Box): ) self.error_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) - self.error_tree = MaskedTree(ERROR) + self.error_tree = MaskedTree(ICON_ERROR) self.error_placeholder = Placeholder(preconnect.no_errors) self.error_box.add(self.error_tree) self.error_box.add(self.error_placeholder) From 49298d99430d06311a32e14acb2526f30a2ca23e Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Mon, 9 Nov 2026 23:51:19 +0900 Subject: [PATCH 04/34] change: normalize endpoint names --- dzgui/api/steam.py | 12 ++++++------ dzgui/const/endpoints.py | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dzgui/api/steam.py b/dzgui/api/steam.py index 4a7d1e8..23357f2 100644 --- a/dzgui/api/steam.py +++ b/dzgui/api/steam.py @@ -31,10 +31,10 @@ from dzgui.const.constants import ( VDF_PATH, ) from dzgui.const.endpoints import ( - APP_DETAILS, - SUB_ENDPOINT, + STEAM_APP_DETAILS, STEAM_PUBLISHED_FILES, - UNSUB_ENDPOINT, + STEAM_SUB, + STEAM_UNSUB, ) from dzgui.strings import wizard from dzgui.strings.errors import api_validation_error, workshop_endpoint_error @@ -310,11 +310,11 @@ def update_workshop(key: str, mod: int, endpoint: str) -> None: def subscribe(key: str, mod: int) -> None: - update_workshop(key, mod, SUB_ENDPOINT) + update_workshop(key, mod, STEAM_SUB) def unsubscribe(key: str, mod: int) -> None: - update_workshop(key, mod, UNSUB_ENDPOINT) + update_workshop(key, mod, STEAM_UNSUB) def validate_mod(client: str, mod: str) -> None: @@ -512,7 +512,7 @@ def get_app_path(folders_path: Path, appid: int) -> Path: def get_app_name(appid: int) -> str | None: payload = {"appids": [appid]} - res = requests.get(APP_DETAILS, params=payload) + res = requests.get(STEAM_APP_DETAILS, params=payload) if res.status_code != 200: return None try: diff --git a/dzgui/const/endpoints.py b/dzgui/const/endpoints.py index 7616ff0..ed6239d 100644 --- a/dzgui/const/endpoints.py +++ b/dzgui/const/endpoints.py @@ -3,9 +3,9 @@ STEAM_PUBLISHED_FILES = ( "https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1" ) STEAM_SERVERS = "https://api.steampowered.com/IGameServersService/GetServerList/v1" -SUB_ENDPOINT = "https://api.steampowered.com/IPublishedFileService/Subscribe/v1" -UNSUB_ENDPOINT = "https://api.steampowered.com/IPublishedFileService/Unsubscribe/v1" -APP_DETAILS = "https://store.steampowered.com/api/appdetails?" +STEAM_SUB = "https://api.steampowered.com/IPublishedFileService/Subscribe/v1" +STEAM_UNSUB = "https://api.steampowered.com/IPublishedFileService/Unsubscribe/v1" +STEAM_APP_DETAILS = "https://store.steampowered.com/api/appdetails?" GITHUB = "https://github.com/aclist" GITHUB_RELEASES = "https://api.github.com/repos/aclist/dztui/releases/latest" From 6901e5456e14f689403cbd9b314b9983a554139b Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 02:36:18 +0900 Subject: [PATCH 05/34] drop: unused dataclass --- dzgui/api/mods.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/dzgui/api/mods.py b/dzgui/api/mods.py index c6a076d..cbb55ad 100644 --- a/dzgui/api/mods.py +++ b/dzgui/api/mods.py @@ -24,14 +24,6 @@ from typing import Any logger = logging.getLogger(APP_NAME) -@dataclass -class ModMeta: - protocol: str - published_id: str - name: str - timestamp: str - - def get_local_mod_ids(steam_path: Path) -> list[int]: workshop_path = get_local_mod_path(steam_path) mods = get_local_mods(workshop_path) From c8fe6ec0567cdf349d7f4b02d063a1eceb9880ac Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 02:37:22 +0900 Subject: [PATCH 06/34] chore: update strings files --- dzgui/strings/buttons.py | 14 ++++++++++++++ dzgui/strings/dialogs.py | 2 ++ dzgui/strings/filters.py | 4 ++++ dzgui/strings/generic.py | 1 + dzgui/strings/notes.py | 5 +++++ dzgui/strings/options.py | 2 ++ dzgui/strings/server_mods.py | 3 ++- 7 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 dzgui/strings/filters.py create mode 100644 dzgui/strings/notes.py diff --git a/dzgui/strings/buttons.py b/dzgui/strings/buttons.py index faa00a0..539b7a0 100644 --- a/dzgui/strings/buttons.py +++ b/dzgui/strings/buttons.py @@ -1,2 +1,16 @@ +clipboard_button = "Copy to clipboard" +clipboard_ip_button = "Copy IP to clipboard" + +dock_button_collapse = "Collapse the connection panel" +dock_button_expand = "Expand the connection panel" + workshop = "Steam Workshop" workshop_tooltip = "Open your Workshop subscriptions" + +version_notifier = "Copied!" +version_tooltip = "Click to copy to clipboard" + +update_button = "Updates available" + +unset_button_label = "Unset" +unset_button_tooltip = "Unset this server as favorite" diff --git a/dzgui/strings/dialogs.py b/dzgui/strings/dialogs.py index b895ba0..b31f58a 100644 --- a/dzgui/strings/dialogs.py +++ b/dzgui/strings/dialogs.py @@ -29,3 +29,5 @@ toggle_calc = "Toggle calculator" server_details = "Server details" server_message = "Server message" + +caught_cancel_signal = "Caught cancel signal, cleaning up" diff --git a/dzgui/strings/filters.py b/dzgui/strings/filters.py new file mode 100644 index 0000000..b6e6bc6 --- /dev/null +++ b/dzgui/strings/filters.py @@ -0,0 +1,4 @@ +all_maps = "All maps" +filters_heading = "Filters" +keyword_placeholder = "Filter by keyword" +map_placeholder = "Filter by map" diff --git a/dzgui/strings/generic.py b/dzgui/strings/generic.py index 1aff78a..68b9cd9 100644 --- a/dzgui/strings/generic.py +++ b/dzgui/strings/generic.py @@ -1 +1,2 @@ +tab_changed_label = "*" selectable_tree = "Ctrl-click to select multiple; Shift-click to select a range." diff --git a/dzgui/strings/notes.py b/dzgui/strings/notes.py new file mode 100644 index 0000000..77d53da --- /dev/null +++ b/dzgui/strings/notes.py @@ -0,0 +1,5 @@ +dialog_title = "Add a short note/reminder" +dialog_subtitle = "Limit 30 chars" +delete_button = "Delete note" +ok_button = "Update note" +current_note = "Current note: '{0}'" diff --git a/dzgui/strings/options.py b/dzgui/strings/options.py index a105f07..fd505f2 100644 --- a/dzgui/strings/options.py +++ b/dzgui/strings/options.py @@ -16,7 +16,9 @@ lan_combo = "LAN" fullscreen_eventbox = "This option is not available on Steam Deck." save_button = "Save" +api_placeholder = "Enter API key" api_failed = "API validation failed" +api_validate = "Validate" flatpak_label = "Flatpak client" flatpak_standard = "Standard" diff --git a/dzgui/strings/server_mods.py b/dzgui/strings/server_mods.py index c41b443..d5a2e42 100644 --- a/dzgui/strings/server_mods.py +++ b/dzgui/strings/server_mods.py @@ -3,4 +3,5 @@ mod_id = "ID" up_to_date = "Up to date" modlist = "Modlist" checkmark = "✓" -resync="⟳" +resync = "⟳" +invalid_mod = "Invalid mod" From fa2f86b03e5fdc286d02909eb79f4eb895d1f55f Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 02:41:24 +0900 Subject: [PATCH 07/34] chore: abstract string literals --- dzgui/managers/connection.py | 8 +++---- dzgui/managers/filter.py | 12 +++------- dzgui/model/proxy_model.py | 3 ++- dzgui/util/strings.py | 3 --- dzgui/views/components/buttons.py | 10 ++++----- dzgui/views/components/connect_panel.py | 18 +++++++-------- dzgui/views/components/entry.py | 7 +++--- dzgui/views/components/filter_panel.py | 21 ++++++++++------- dzgui/views/components/right_panel.py | 9 ++++---- dzgui/views/dialogs/generic.py | 3 +-- dzgui/views/dialogs/note.py | 30 +++++++++---------------- dzgui/views/dialogs/wizard.py | 2 +- dzgui/views/pages/servers.py | 4 ++-- 13 files changed, 56 insertions(+), 74 deletions(-) diff --git a/dzgui/managers/connection.py b/dzgui/managers/connection.py index 89c35f0..19a69d2 100644 --- a/dzgui/managers/connection.py +++ b/dzgui/managers/connection.py @@ -48,7 +48,7 @@ from dzgui.strings.dialogs import ( waiting_for_directories, ) from dzgui.strings import kb -from dzgui.strings.server_mods import checkmark, resync +from dzgui.strings.server_mods import checkmark, resync, invalid_mod from dzgui.util.format import format_exception, format_mib from dzgui.util.strings import dialog, server_timeout from dzgui.util.symlink import rebuild_symlinks @@ -197,9 +197,8 @@ class ConnectionManager: is_last = self.is_last_server() - # TODO: strings invalid_mods = [ - (mod[0], mod[1]) for mod in remote_mods if mod[2] == "Invalid mod" + (mod[0], mod[1]) for mod in remote_mods if mod[2] == invalid_mod ] prereqs = Prerequisites( @@ -277,8 +276,7 @@ class ConnectionManager: for mod in alpha_mods: # NOTE: if the mod is neither synched or out of date, it is a malformed mod if mod[2] == "": - # TODO: strings - mod[2] = "Invalid mod" + mod[2] = invalid_mod return alpha_mods, missing_mods diff --git a/dzgui/managers/filter.py b/dzgui/managers/filter.py index 4151bc5..ffb41ef 100644 --- a/dzgui/managers/filter.py +++ b/dzgui/managers/filter.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING from dzgui.model.model_factory import ModelFactory from dzgui.util import strings -from dzgui.util.strings import all_maps +from dzgui.strings.filters import all_maps if TYPE_CHECKING: from dzgui.model.model_factory import FastInsertListStore @@ -106,16 +106,10 @@ class FilterManager: self.reinit_map_store() maps.sort() - for m in maps: - # TODO: strings - if m == "All maps": - continue + clean = [m for m in maps if m != all_maps] + for m in clean: self.append_map([m]) - # TODO: currently unused - def get_unique_maps(self) -> list[str]: - return [row[0] for row in self.map_store if row[0] != "All maps"] - def get_all_filters(self) -> tuple: map_name = self.get_active_map_name() enabled = self.get_filters() diff --git a/dzgui/model/proxy_model.py b/dzgui/model/proxy_model.py index c892110..0ec9a12 100644 --- a/dzgui/model/proxy_model.py +++ b/dzgui/model/proxy_model.py @@ -5,6 +5,7 @@ from typing import Any, TYPE_CHECKING, Union from dzgui.const.enum import FilterMode from dzgui.model.model_factory import ModelFactory from dzgui.util import strings +from dzgui.strings import filters as str_filters if TYPE_CHECKING: from dzgui.managers.filter import FilterManager @@ -175,7 +176,7 @@ class ProxyModelManager: rows = self.filtered sel_map = self.filter_man.get_active_map_name() - if sel_map == strings.all_maps: + if sel_map == str_filters.all_maps: return rows rows = [row for row in rows if row[1] == sel_map] diff --git a/dzgui/util/strings.py b/dzgui/util/strings.py index 931ca88..095d49a 100644 --- a/dzgui/util/strings.py +++ b/dzgui/util/strings.py @@ -159,9 +159,6 @@ filter_unofficial = "Unofficial" filter_modded = "Modded" filter_unmodded = "Unmodded" -# maps -all_maps = "All maps" - # state unknown = "Unknown" none_provided = "None provided" diff --git a/dzgui/views/components/buttons.py b/dzgui/views/components/buttons.py index bb3d45d..7306319 100644 --- a/dzgui/views/components/buttons.py +++ b/dzgui/views/components/buttons.py @@ -106,8 +106,7 @@ class ClipboardButton(IconTextButton): self.controller = controller self.connect("clicked", self._on_button_clicked, func) - # TODO: strings - self.set_tooltip_text("Copy to clipboard") + self.set_tooltip_text(buttons.clipboard_button) def _on_button_clicked(self, button: Self, func: Callable) -> None: def revert() -> Literal[False]: @@ -125,16 +124,14 @@ class CopyIpButton(ClipboardButton): def __init__(self, controller: Union["Controller", None], func: Callable) -> None: super().__init__(controller, func) - # TODO: strings - self.set_tooltip_text("Copy IP to clipboard") + self.set_tooltip_text(buttons.clipboard_ip_button) class CloseButton(IconTextButton): def __init__(self, label: str) -> None: super().__init__(icon=CLOSE, label=label) - # TODO: strings - self.set_tooltip_text("Unset this server as favorite") + self.set_tooltip_text(buttons.unset_button_tooltip) class WebButton(IconTextButton): @@ -252,6 +249,7 @@ class AddButton(IconTextButton): self.set_tooltip_text(connect_panel.add_tooltip) +# TODO: drop class LoggerAlertsButton(IconTextButton): def __init__(self, warnings: int, errors: int) -> None: warnings_text = "" diff --git a/dzgui/views/components/connect_panel.py b/dzgui/views/components/connect_panel.py index 17d0a71..da1ecd4 100644 --- a/dzgui/views/components/connect_panel.py +++ b/dzgui/views/components/connect_panel.py @@ -3,6 +3,11 @@ from typing import Self, TYPE_CHECKING from dzgui.const.constants import CARET_DOWN, CARET_UP, UDP_PORT from dzgui.model.servers import ServerModelManager from dzgui.strings import connect_panel +from dzgui.strings.buttons import ( + dock_button_collapse, + dock_button_expand, + unset_button_label, +) from dzgui.util.keys import is_ctrl_mask from dzgui.views.components.buttons import ( AddButton, @@ -36,21 +41,18 @@ class DockButton(IconButton): self.child = child self.connect("clicked", self._on_click) - self.collapse_text = "Collapse the connection panel" - self.expand_text = "Expand the connection panel" - self.set_margin_top(10) - self.set_tooltip_text(self.collapse_text) + self.set_tooltip_text(dock_button_collapse) def collapse(self) -> None: self.set_image(Icon(CARET_UP)) self.child.hide() - self.set_tooltip_text(self.expand_text) + self.set_tooltip_text(dock_button_expand) def expand(self) -> None: self.set_image(Icon(CARET_DOWN)) self.child.show() - self.set_tooltip_text(self.collapse_text) + self.set_tooltip_text(dock_button_collapse) def _on_click(self, button: Self) -> None: if self.expanded: @@ -81,7 +83,6 @@ class LanPanel(Gtk.Frame): self.scan = Gtk.Button(label=connect_panel.lan_scan_button) self.scan.connect("clicked", self._on_scan_clicked) - # TODO: strings self.early_abort = Gtk.CheckButton(label=connect_panel.lan_checkbox) self.early_abort.set_active(True) self.early_abort.set_tooltip_text(connect_panel.lan_abort_tooltip) @@ -189,8 +190,7 @@ class FavPanel(Gtk.Frame): self.fav_button.connect("clicked", self._on_connect_clicked) self.copy_button = CopyIpButton(self.controller, self.get_fav_ip) - # TODO: strings - self.unset_button = CloseButton("Unset") + self.unset_button = CloseButton(unset_button_label) self.unset_button.connect("clicked", self._on_unset_clicked) if favorite is None: diff --git a/dzgui/views/components/entry.py b/dzgui/views/components/entry.py index e1b3e7e..ef29d71 100644 --- a/dzgui/views/components/entry.py +++ b/dzgui/views/components/entry.py @@ -4,6 +4,7 @@ from dzgui.api.servers import validate_ip from dzgui.const.constants import VIEW_CONCEAL, VIEW_REVEAL from dzgui.util.css import add_class, remove_class from dzgui.strings import connect_panel +from dzgui.strings.options import api_placeholder, api_validate from dzgui.strings.errors import api_popover import gi @@ -143,9 +144,8 @@ class APIEntry(Gtk.Box): self.func: Callable | None = None - # TODO: strings self.entry = Gtk.Entry( - width_chars=60, hexpand=True, placeholder_text="Enter API key" + width_chars=60, hexpand=True, placeholder_text=api_placeholder ) self.entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, VIEW_REVEAL) self.entry.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, True) @@ -154,8 +154,7 @@ class APIEntry(Gtk.Box): self.entry.connect("icon-release", self._on_icon_release) self.entry.connect("activate", self._on_field_activated) - # TODO: strings - self.submit = Gtk.Button(label="Validate") + self.submit = Gtk.Button(label=api_validate) self.submit.set_sensitive(False) self.submit.connect("clicked", self._on_submit) diff --git a/dzgui/views/components/filter_panel.py b/dzgui/views/components/filter_panel.py index 75c51e9..b715835 100644 --- a/dzgui/views/components/filter_panel.py +++ b/dzgui/views/components/filter_panel.py @@ -10,7 +10,12 @@ from dzgui.const.constants import ( SEARCH_ICON, ) from dzgui.util.format import format_exception -from dzgui.util.strings import all_maps +from dzgui.strings.filters import ( + all_maps, + filters_heading, + keyword_placeholder, + map_placeholder, +) from dzgui.views.components.maps_combo import MapsCombo from dzgui.views.components.labels import BoldLabel @@ -42,7 +47,10 @@ class ButtonGrid(Gtk.Grid): self.checks: list[Gtk.CheckButton] = [] flowbox = Gtk.FlowBox( - halign=Gtk.Align.CENTER, min_children_per_line=3, max_children_per_line=3, selection_mode=Gtk.SelectionMode.NONE + halign=Gtk.Align.CENTER, + min_children_per_line=3, + max_children_per_line=3, + selection_mode=Gtk.SelectionMode.NONE, ) for check in defaults.keys(): @@ -99,8 +107,7 @@ class ButtonGrid(Gtk.Grid): class KeywordEntry(Gtk.Entry): def __init__(self, controller: "Controller") -> None: - # TODO :strings - super().__init__(placeholder_text="Filter by keyword") + super().__init__(placeholder_text=keyword_placeholder) self.keyword = "" self.controller = controller @@ -174,8 +181,7 @@ class FilterPanel(Gtk.Box): self.keyword_entry = KeywordEntry(self.controller) self.button_grid = ButtonGrid(self.controller, defaults, tooltips) - # TODO: strings - self.filters_label = BoldLabel("Filters") + self.filters_label = BoldLabel(filters_heading) self.emitter.connect( "request_keyword_focus", lambda _: self.keyword_entry.grab_focus() @@ -195,8 +201,7 @@ class FilterPanel(Gtk.Box): self.maps_entry: Gtk.Entry = self.maps_combo.get_child() # type: ignore self.maps_entry.set_completion(completion) - # TODO: strings - self.maps_entry.set_placeholder_text("Filter by map") + self.maps_entry.set_placeholder_text(map_placeholder) self.maps_entry.connect("changed", self._on_map_completion, True) self.maps_entry.connect("key-press-event", self._on_map_entry_keypress) self.maps_entry.connect("activate", self._on_maps_activated) diff --git a/dzgui/views/components/right_panel.py b/dzgui/views/components/right_panel.py index f38def4..7e0f753 100644 --- a/dzgui/views/components/right_panel.py +++ b/dzgui/views/components/right_panel.py @@ -13,6 +13,7 @@ from dzgui.const.constants import ( ) from dzgui.const.enum import ServerTab from dzgui.managers.update import UpdateManager +from dzgui.strings.buttons import update_button, version_notifier, version_tooltip from dzgui.util.clip import copy_clipboard from dzgui.util.file import is_writeable from dzgui.views.components.buttonbox import ButtonBox @@ -65,8 +66,7 @@ class RightPanel(Gtk.Box): vexpand=True, halign=Gtk.Align.END, valign=Gtk.Align.END, - # TODO: strings - tooltip_text="Click to copy to clipboard", + tooltip_text=version_tooltip, ) eb = Gtk.EventBox(halign=Gtk.Align.END, valign=Gtk.Align.END) eb.add(self.version_label) @@ -78,7 +78,7 @@ class RightPanel(Gtk.Box): self.pack_start(self.sel_panel, NO_EXPAND, NO_FILL, NO_PADDING) self.update_button = IconTextButton( - "dialog-information-symbolic", label="Updates available" + "dialog-information-symbolic", label=update_button ) self.gutter_box = Gtk.Box( @@ -131,7 +131,6 @@ class RightPanel(Gtk.Box): return self.copying = True version = self.version_label.get_text() - # TODO: strings - self.version_label.set_text("Copied!") + self.version_label.set_text(version_notifier) copy_clipboard(version) GLib.timeout_add_seconds(1, revert) diff --git a/dzgui/views/dialogs/generic.py b/dzgui/views/dialogs/generic.py index cff1e2a..ad355e9 100644 --- a/dzgui/views/dialogs/generic.py +++ b/dzgui/views/dialogs/generic.py @@ -140,8 +140,7 @@ class WaitDialog(GenericDialog): self.cur_job += 1 def _on_cancel_clicked(self, button: Gtk.Button) -> None: - # TODO: strings - GLib.idle_add(self.update_text, "Caught cancel signal, cleaning up") + GLib.idle_add(self.update_text, dialogs.caught_cancel_signal) self.controller.set_cancel_event() def show_cancel(self, state: bool) -> None: diff --git a/dzgui/views/dialogs/note.py b/dzgui/views/dialogs/note.py index b595e90..362f1d8 100644 --- a/dzgui/views/dialogs/note.py +++ b/dzgui/views/dialogs/note.py @@ -1,12 +1,13 @@ from typing import Self, TYPE_CHECKING +from dzgui.strings import notes from dzgui.views.components.entry import ValidatedEntry from dzgui.views.dialogs.generic import GenericDialog -import gi # noqa E402 +import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gdk, Gtk # noqa E402 +from gi.repository import Gtk if TYPE_CHECKING: from dzgui.controllers.mc import Controller @@ -14,23 +15,20 @@ if TYPE_CHECKING: class NoteDialog(GenericDialog): def __init__(self, controller: "Controller"): - # TODO: add to strings - title = "Add a short note/reminder." - secondary = "Limit 30 chars" super().__init__( controller=controller, - text=title, + text=notes.dialog_title, buttons=Gtk.ButtonsType.NONE, mtype=Gtk.MessageType.INFO, - secondary=secondary, + secondary=notes.dialog_subtitle, ) self.controller = controller - delete_button = Gtk.Button(label="Delete note") + delete_button = Gtk.Button(label=notes.delete_button) delete_button.set_sensitive(False) self.add_action_widget(delete_button, Gtk.ResponseType.CANCEL) - self.ok = self.add_button("Update note", Gtk.ResponseType.OK) + self.ok = self.add_button(notes.ok_button, Gtk.ResponseType.OK) self.ok.grab_default() self.entry = ValidatedEntry(controller, self.validate) @@ -42,7 +40,7 @@ class NoteDialog(GenericDialog): self.entry.set_text(note) if len(note) > 0: delete_button.set_sensitive(True) - self.format_secondary_text(f"Current note: '{note}'") + self.format_secondary_text(notes.current_note.format(note)) elif len(note) == 0: self.ok.set_sensitive(False) @@ -55,9 +53,6 @@ class NoteDialog(GenericDialog): self.entry.connect("changed", self._on_text_changed) def _on_text_changed(self, entry: Gtk.Entry) -> None: - """ - Called in conjunction with signal callback in the parent class - """ if len(entry.get_text()) == 0: self.ok.set_sensitive(False) @@ -71,14 +66,11 @@ class NoteDialog(GenericDialog): def _on_response(self, dialog: Self, response: Gtk.ResponseType) -> None: match response: - case Gtk.ResponseType.DELETE_EVENT: - self.destroy() case Gtk.ResponseType.OK: note = self.entry.get_text() self.controller.add_note(note) - self.destroy() case Gtk.ResponseType.CANCEL: self.controller.delete_note() - self.destroy() - case Gtk.ResponseType.CANCEL | Gtk.ResponseType.CLOSE: - self.destroy() + case _: + pass + self.destroy() diff --git a/dzgui/views/dialogs/wizard.py b/dzgui/views/dialogs/wizard.py index 5dbcdb4..8591ba3 100644 --- a/dzgui/views/dialogs/wizard.py +++ b/dzgui/views/dialogs/wizard.py @@ -14,7 +14,7 @@ from dzgui.const.constants import ( HERO_PATH, FLATPAK_ROOT_PATH, LEGACY_CONFIG_PATH, - TMP_PATH + TMP_PATH, ) from dzgui.const.boilerplate import config_boilerplate from dzgui.const.endpoints import STEAM_API_SETUP diff --git a/dzgui/views/pages/servers.py b/dzgui/views/pages/servers.py index caefe21..c482929 100644 --- a/dzgui/views/pages/servers.py +++ b/dzgui/views/pages/servers.py @@ -5,6 +5,7 @@ from typing import Self, TYPE_CHECKING from dzgui.const.constants import APP_NAME from dzgui.const.enum import ContextMenuGroup, ServerTab from dzgui.views.trees.tree_servers import ServerTreeView +from dzgui.strings.generic import tab_changed_label from dzgui.util.strings import server_labels import gi @@ -147,8 +148,7 @@ class ServerNotebook(Gtk.ScrolledWindow): label = self.notebook.get_tab_label_text(child) if label is not None: - # TODO: strings - text = label.strip("*") + text = label.strip(tab_changed_label) self.notebook.set_tab_label_text(child, text) tree = child.get_tree() From 3f29484895e110834f9d3da80017934e6d3111d5 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 02:59:22 +0900 Subject: [PATCH 08/34] chore: drop unused Gtk imports --- dzgui/controllers/emitter.py | 4 ++-- dzgui/controllers/mc.py | 2 +- dzgui/managers/connection.py | 5 ----- dzgui/managers/mods.py | 2 +- dzgui/managers/threading.py | 2 +- dzgui/managers/update.py | 5 ----- dzgui/model/model_factory.py | 4 ++-- dzgui/model/servers.py | 2 +- dzgui/util/clip.py | 3 +-- dzgui/util/css.py | 2 +- dzgui/util/dist.py | 5 ----- dzgui/util/format.py | 2 +- dzgui/util/keys.py | 5 ++++- dzgui/util/margins.py | 4 +++- dzgui/util/save_screenshot.py | 2 +- dzgui/views/base.py | 2 +- dzgui/views/components/buttonbox.py | 2 +- dzgui/views/components/buttons.py | 2 +- dzgui/views/components/connect_panel.py | 2 +- dzgui/views/components/crumbs.py | 2 +- dzgui/views/components/entry.py | 2 +- dzgui/views/components/eventbox.py | 2 +- dzgui/views/components/filter_panel.py | 7 +++++-- dzgui/views/components/icon.py | 2 +- dzgui/views/components/labels.py | 2 +- dzgui/views/components/maps_combo.py | 2 +- dzgui/views/components/mod_panel.py | 2 +- dzgui/views/components/right_panel.py | 2 +- dzgui/views/components/statusbar.py | 2 +- dzgui/views/components/toast.py | 3 ++- dzgui/views/dialogs/calc.py | 2 +- dzgui/views/dialogs/early_alert.py | 2 +- dzgui/views/dialogs/filepicker.py | 4 ++-- dzgui/views/dialogs/generic.py | 2 +- dzgui/views/dialogs/link_dialog.py | 2 +- dzgui/views/dialogs/note.py | 4 ++-- dzgui/views/dialogs/uninstall.py | 2 +- dzgui/views/dialogs/wizard.py | 4 ++-- dzgui/views/mixins/context_mixin.py | 2 +- dzgui/views/mixins/cursor_mixin.py | 2 +- dzgui/views/mixins/mods_mixin.py | 2 +- dzgui/views/pages/changelog.py | 2 +- dzgui/views/pages/devs.py | 4 ++-- dzgui/views/pages/help.py | 2 +- dzgui/views/pages/keys.py | 2 +- dzgui/views/pages/log.py | 2 +- dzgui/views/pages/preconnect.py | 2 +- dzgui/views/pages/servers.py | 2 +- dzgui/views/pages/thanks.py | 2 +- dzgui/views/trees/tree_log.py | 2 +- pyproject.toml | 1 + tests/test_html_escaping.py | 2 +- 52 files changed, 65 insertions(+), 71 deletions(-) diff --git a/dzgui/controllers/emitter.py b/dzgui/controllers/emitter.py index 82b0e91..e21ac18 100644 --- a/dzgui/controllers/emitter.py +++ b/dzgui/controllers/emitter.py @@ -3,8 +3,8 @@ from typing import TYPE_CHECKING import gi gi.require_version("Gtk", "3.0") -from gi.repository.Gtk import ListStore # noqa E402 -from gi.repository import Gdk, GObject # noqa E402 +from gi.repository.Gtk import ListStore +from gi.repository import GObject if TYPE_CHECKING: from dzgui.const.enum import NotebookPage, ServerTab diff --git a/dzgui/controllers/mc.py b/dzgui/controllers/mc.py index 1a05955..5c5a532 100644 --- a/dzgui/controllers/mc.py +++ b/dzgui/controllers/mc.py @@ -32,7 +32,7 @@ from dzgui.views.dialogs.generic import ExceptionDialog import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, Gdk, GLib, GObject # noqa E402 +from gi.repository import Gtk, Gdk, GLib, GObject logger = logging.getLogger(APP_NAME) diff --git a/dzgui/managers/connection.py b/dzgui/managers/connection.py index 89c35f0..cb9faf2 100644 --- a/dzgui/managers/connection.py +++ b/dzgui/managers/connection.py @@ -55,11 +55,6 @@ from dzgui.util.symlink import rebuild_symlinks from dzgui.views.dialogs.generic import ExceptionDialog from dzgui.views.dialogs.servers import ServerDetailsDialog, ServerModDialog -import gi - -gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, GLib # noqa E402 - if TYPE_CHECKING: from dzgui.api.servers import A2SInfo, Record from dzgui.controllers.mc import Controller diff --git a/dzgui/managers/mods.py b/dzgui/managers/mods.py index 6a98e02..421a95f 100644 --- a/dzgui/managers/mods.py +++ b/dzgui/managers/mods.py @@ -30,7 +30,7 @@ import dzgui.api.pefile as PeFile import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa E402 +from gi.repository import Gtk if TYPE_CHECKING: from dzgui.controllers.mc import Controller diff --git a/dzgui/managers/threading.py b/dzgui/managers/threading.py index c20f324..43b89aa 100644 --- a/dzgui/managers/threading.py +++ b/dzgui/managers/threading.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, GLib # noqa E402 +from gi.repository import GLib logger = logging.getLogger(APP_NAME) diff --git a/dzgui/managers/update.py b/dzgui/managers/update.py index ff64fb4..f519c4f 100644 --- a/dzgui/managers/update.py +++ b/dzgui/managers/update.py @@ -15,11 +15,6 @@ from dzgui.strings import dialogs from dzgui.managers.threading import call_on_thread, StoredFunc, ThreadingManager from dzgui.views.dialogs.generic import ExceptionDialog, QuitDialog -import gi - -gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, GLib # noqa E402 - if TYPE_CHECKING: from dzgui.controllers.mc import Controller diff --git a/dzgui/model/model_factory.py b/dzgui/model/model_factory.py index 7dfffbf..dc90f86 100644 --- a/dzgui/model/model_factory.py +++ b/dzgui/model/model_factory.py @@ -7,8 +7,8 @@ from dzgui.util.strings import delimiter import gi gi.require_version("Gtk", "3.0") -from gi.repository.Gtk import ListStore, TreeIter # noqa E402 -from gi.repository import GObject # noqa E402 +from gi.repository.Gtk import ListStore, TreeIter +from gi.repository import GObject GTYPE_TO_PYTHON = { GObject.type_from_name(GObject.type_name(ptype)): ptype diff --git a/dzgui/model/servers.py b/dzgui/model/servers.py index a1d3082..93f2fce 100644 --- a/dzgui/model/servers.py +++ b/dzgui/model/servers.py @@ -21,7 +21,7 @@ from dzgui.views.dialogs.generic import ExceptionDialog import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, GLib # noqa E402 +from gi.repository import Gtk, GLib if TYPE_CHECKING: from dzgui.api.servers import A2SInfo, Record diff --git a/dzgui/util/clip.py b/dzgui/util/clip.py index 971515c..991346d 100644 --- a/dzgui/util/clip.py +++ b/dzgui/util/clip.py @@ -1,8 +1,7 @@ import gi gi.require_version("Gtk", "3.0") -gi.require_version("Gdk", "3.0") -from gi.repository import Gtk, Gdk # noqa E402 +from gi.repository import Gtk, Gdk def copy_clipboard(text: str) -> None: diff --git a/dzgui/util/css.py b/dzgui/util/css.py index 44bf7cd..2350be7 100644 --- a/dzgui/util/css.py +++ b/dzgui/util/css.py @@ -5,7 +5,7 @@ from dzgui.const.constants import APP_NAME_LOWER, CSS_PATH import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, Gdk # noqa E402 +from gi.repository import Gtk, Gdk def add_class(widget: Gtk.Widget, label: str) -> None: diff --git a/dzgui/util/dist.py b/dzgui/util/dist.py index 46c2d44..86f9aa1 100644 --- a/dzgui/util/dist.py +++ b/dzgui/util/dist.py @@ -7,11 +7,6 @@ from dzgui.const.constants import APP_NAME from dzgui.util.ip import get_coords from dzgui.util.localize import number -import gi - -gi.require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa E402 - if TYPE_CHECKING: from queue import Queue from dzgui.const.enum import ServerTab diff --git a/dzgui/util/format.py b/dzgui/util/format.py index e7b4d07..3d75e30 100644 --- a/dzgui/util/format.py +++ b/dzgui/util/format.py @@ -7,7 +7,7 @@ from dzgui.util.strings import no_mods, no_servers, workshop import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa E402 +from gi.repository import Gtk def format_pango(text: str) -> str: diff --git a/dzgui/util/keys.py b/dzgui/util/keys.py index 04f291d..5293d21 100644 --- a/dzgui/util/keys.py +++ b/dzgui/util/keys.py @@ -1,6 +1,8 @@ import gi + gi.require_version("Gtk", "3.0") -from gi.repository import Gdk # noqa E402 +from gi.repository import Gdk + def is_navkey(key: int) -> bool: nav_keys = [ @@ -18,6 +20,7 @@ def is_navkey(key: int) -> bool: return True return False + def is_ctrl_mask(event: Gdk.EventKey) -> bool: if event.state is Gdk.ModifierType.CONTROL_MASK: return True diff --git a/dzgui/util/margins.py b/dzgui/util/margins.py index f4f8883..82cef96 100644 --- a/dzgui/util/margins.py +++ b/dzgui/util/margins.py @@ -1,6 +1,8 @@ import gi + gi.require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa E402 +from gi.repository import Gtk + def set_surrounding_margins(widget: Gtk.Widget, margin: int) -> None: """ diff --git a/dzgui/util/save_screenshot.py b/dzgui/util/save_screenshot.py index 8c106bc..ffa769f 100644 --- a/dzgui/util/save_screenshot.py +++ b/dzgui/util/save_screenshot.py @@ -4,7 +4,7 @@ from typing import cast, TYPE_CHECKING import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, Gdk, GLib # noqa E402 +from gi.repository import Gdk, GLib if TYPE_CHECKING: from dzgui.controllers.mc import Controller diff --git a/dzgui/views/base.py b/dzgui/views/base.py index 79fde50..4ce0286 100644 --- a/dzgui/views/base.py +++ b/dzgui/views/base.py @@ -38,7 +38,7 @@ import gi gi.require_version("Gtk", "3.0") gi.require_version("GLibUnix", "2.0") -from gi.repository import Gtk, GLib, GLibUnix, Gdk # type: ignore # noqa E402 +from gi.repository import Gtk, GLib, GLibUnix, Gdk if TYPE_CHECKING: from dzgui.config.userprefs import UserPrefs diff --git a/dzgui/views/components/buttonbox.py b/dzgui/views/components/buttonbox.py index 496f237..fedad93 100644 --- a/dzgui/views/components/buttonbox.py +++ b/dzgui/views/components/buttonbox.py @@ -7,7 +7,7 @@ from dzgui.const.enum import ButtonType import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, Gdk # noqa E402 +from gi.repository import Gtk, Gdk logger = logging.getLogger(APP_NAME) diff --git a/dzgui/views/components/buttons.py b/dzgui/views/components/buttons.py index bb3d45d..3bc72fa 100644 --- a/dzgui/views/components/buttons.py +++ b/dzgui/views/components/buttons.py @@ -22,7 +22,7 @@ from dzgui.strings import connect_panel import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, Gdk, GLib # noqa E402 +from gi.repository import Gtk, GLib if TYPE_CHECKING: from gi.repository import GLib diff --git a/dzgui/views/components/connect_panel.py b/dzgui/views/components/connect_panel.py index 17d0a71..0e1440c 100644 --- a/dzgui/views/components/connect_panel.py +++ b/dzgui/views/components/connect_panel.py @@ -18,7 +18,7 @@ from dzgui.views.components.labels import BoldLabel import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, Gdk # noqa E402 +from gi.repository import Gtk, Gdk if TYPE_CHECKING: from dzgui.controllers.mc import Controller diff --git a/dzgui/views/components/crumbs.py b/dzgui/views/components/crumbs.py index cd57426..610f8d6 100644 --- a/dzgui/views/components/crumbs.py +++ b/dzgui/views/components/crumbs.py @@ -7,7 +7,7 @@ from dzgui.util.format import embolden import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa E402 +from gi.repository import Gtk if TYPE_CHECKING: from dzgui.controllers.mc import Controller diff --git a/dzgui/views/components/entry.py b/dzgui/views/components/entry.py index e1b3e7e..47a036a 100644 --- a/dzgui/views/components/entry.py +++ b/dzgui/views/components/entry.py @@ -9,7 +9,7 @@ from dzgui.strings.errors import api_popover import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, Gdk, GObject # noqa E402 +from gi.repository import Gtk, Gdk, GObject if TYPE_CHECKING: from dzgui.controllers.mc import Controller diff --git a/dzgui/views/components/eventbox.py b/dzgui/views/components/eventbox.py index 5e06127..586b48e 100644 --- a/dzgui/views/components/eventbox.py +++ b/dzgui/views/components/eventbox.py @@ -6,7 +6,7 @@ from dzgui.const.constants import HELP_BUBBLE import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, Gdk # noqa E402 +from gi.repository import Gtk, Gdk if TYPE_CHECKING: from dzgui.controllers.mc import Controller diff --git a/dzgui/views/components/filter_panel.py b/dzgui/views/components/filter_panel.py index 75c51e9..fa996c0 100644 --- a/dzgui/views/components/filter_panel.py +++ b/dzgui/views/components/filter_panel.py @@ -17,7 +17,7 @@ from dzgui.views.components.labels import BoldLabel import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, Gdk, Pango, GLib # noqa E402 +from gi.repository import Gtk, Gdk, Pango, GLib logger = logging.getLogger(APP_NAME) @@ -42,7 +42,10 @@ class ButtonGrid(Gtk.Grid): self.checks: list[Gtk.CheckButton] = [] flowbox = Gtk.FlowBox( - halign=Gtk.Align.CENTER, min_children_per_line=3, max_children_per_line=3, selection_mode=Gtk.SelectionMode.NONE + halign=Gtk.Align.CENTER, + min_children_per_line=3, + max_children_per_line=3, + selection_mode=Gtk.SelectionMode.NONE, ) for check in defaults.keys(): diff --git a/dzgui/views/components/icon.py b/dzgui/views/components/icon.py index 2cb222d..54b88d9 100644 --- a/dzgui/views/components/icon.py +++ b/dzgui/views/components/icon.py @@ -1,7 +1,7 @@ import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa E402 +from gi.repository import Gtk class Icon(Gtk.Image): diff --git a/dzgui/views/components/labels.py b/dzgui/views/components/labels.py index 9ecc52a..a9c7db0 100644 --- a/dzgui/views/components/labels.py +++ b/dzgui/views/components/labels.py @@ -3,7 +3,7 @@ from dzgui.util.format import embolden import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa E402 +from gi.repository import Gtk class BoldLabel(Gtk.Label): diff --git a/dzgui/views/components/maps_combo.py b/dzgui/views/components/maps_combo.py index 0eea10f..821b831 100644 --- a/dzgui/views/components/maps_combo.py +++ b/dzgui/views/components/maps_combo.py @@ -1,7 +1,7 @@ import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, GLib # noqa E402 +from gi.repository import Gtk class MapsCombo(Gtk.EntryCompletion): diff --git a/dzgui/views/components/mod_panel.py b/dzgui/views/components/mod_panel.py index 7c913c4..bcc74b7 100644 --- a/dzgui/views/components/mod_panel.py +++ b/dzgui/views/components/mod_panel.py @@ -8,7 +8,7 @@ from dzgui.views.components.labels import BoldLabel import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa E402 +from gi.repository import Gtk if TYPE_CHECKING: from dzgui.controllers.mc import Controller diff --git a/dzgui/views/components/right_panel.py b/dzgui/views/components/right_panel.py index f38def4..08b6681 100644 --- a/dzgui/views/components/right_panel.py +++ b/dzgui/views/components/right_panel.py @@ -23,7 +23,7 @@ from dzgui.views.components.buttons import IconTextButton, RefreshButton, KeysBu import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, Gdk, GLib # noqa E402 +from gi.repository import Gtk, Gdk, GLib if TYPE_CHECKING: from dzgui.controllers.mc import Controller diff --git a/dzgui/views/components/statusbar.py b/dzgui/views/components/statusbar.py index 7eb75a3..6726021 100644 --- a/dzgui/views/components/statusbar.py +++ b/dzgui/views/components/statusbar.py @@ -7,7 +7,7 @@ from dzgui.views.components.buttons import LoggerAlertsButton import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa E402 +from gi.repository import Gtk if TYPE_CHECKING: from dzgui.const.enum import ServerTab diff --git a/dzgui/views/components/toast.py b/dzgui/views/components/toast.py index d6fd577..95e663f 100644 --- a/dzgui/views/components/toast.py +++ b/dzgui/views/components/toast.py @@ -2,8 +2,9 @@ from typing import Literal from dzgui.util import css import gi + gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, GLib # noqa E402 +from gi.repository import Gtk, GLib class Toast(Gtk.EventBox): diff --git a/dzgui/views/dialogs/calc.py b/dzgui/views/dialogs/calc.py index 979cb0a..e5d6819 100644 --- a/dzgui/views/dialogs/calc.py +++ b/dzgui/views/dialogs/calc.py @@ -10,7 +10,7 @@ from dzgui.views.components.labels import BoldLabel from dzgui.views.components.box import VBox, HBox gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, GLib, GObject # type: ignore # noqa E402 +from gi.repository import Gtk, GLib, GObject DAY_START_TIME = time(hour=6, minute=0, second=0) DAY_END_TIME = time(hour=18, minute=0, second=0) diff --git a/dzgui/views/dialogs/early_alert.py b/dzgui/views/dialogs/early_alert.py index b7032a0..702560b 100644 --- a/dzgui/views/dialogs/early_alert.py +++ b/dzgui/views/dialogs/early_alert.py @@ -8,7 +8,7 @@ from dzgui.views.mixins.colorscheme import ColorAwareApp import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa E402 +from gi.repository import Gtk class AbortDialog(ColorAwareApp, Gtk.MessageDialog): # type: ignore diff --git a/dzgui/views/dialogs/filepicker.py b/dzgui/views/dialogs/filepicker.py index 2048598..15371c3 100644 --- a/dzgui/views/dialogs/filepicker.py +++ b/dzgui/views/dialogs/filepicker.py @@ -2,10 +2,10 @@ from pathlib import Path from dzgui.util.strings import picker -import gi # noqa E402 +import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa E402 +from gi.repository import Gtk class FilePicker(Gtk.FileChooserDialog): diff --git a/dzgui/views/dialogs/generic.py b/dzgui/views/dialogs/generic.py index cff1e2a..65b79d5 100644 --- a/dzgui/views/dialogs/generic.py +++ b/dzgui/views/dialogs/generic.py @@ -8,7 +8,7 @@ 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 +from gi.repository import Gtk, GLib if TYPE_CHECKING: from dzgui.controllers.mc import Controller diff --git a/dzgui/views/dialogs/link_dialog.py b/dzgui/views/dialogs/link_dialog.py index 294765c..1a5d6ff 100644 --- a/dzgui/views/dialogs/link_dialog.py +++ b/dzgui/views/dialogs/link_dialog.py @@ -9,7 +9,7 @@ from dzgui.views.components.buttons import SteamTextButton import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa E402 +from gi.repository import Gtk if TYPE_CHECKING: diff --git a/dzgui/views/dialogs/note.py b/dzgui/views/dialogs/note.py index b595e90..a1cf7fb 100644 --- a/dzgui/views/dialogs/note.py +++ b/dzgui/views/dialogs/note.py @@ -3,10 +3,10 @@ from typing import Self, TYPE_CHECKING from dzgui.views.components.entry import ValidatedEntry from dzgui.views.dialogs.generic import GenericDialog -import gi # noqa E402 +import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gdk, Gtk # noqa E402 +from gi.repository import Gtk if TYPE_CHECKING: from dzgui.controllers.mc import Controller diff --git a/dzgui/views/dialogs/uninstall.py b/dzgui/views/dialogs/uninstall.py index 3e7d2e7..c649c8a 100644 --- a/dzgui/views/dialogs/uninstall.py +++ b/dzgui/views/dialogs/uninstall.py @@ -18,7 +18,7 @@ from dzgui.views.mixins.colorscheme import ColorAwareApp import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, GLib, Pango # noqa E402 +from gi.repository import Gtk, GLib, Pango class Assistant(Gtk.Assistant): diff --git a/dzgui/views/dialogs/wizard.py b/dzgui/views/dialogs/wizard.py index 5dbcdb4..b3aa618 100644 --- a/dzgui/views/dialogs/wizard.py +++ b/dzgui/views/dialogs/wizard.py @@ -14,7 +14,7 @@ from dzgui.const.constants import ( HERO_PATH, FLATPAK_ROOT_PATH, LEGACY_CONFIG_PATH, - TMP_PATH + TMP_PATH, ) from dzgui.const.boilerplate import config_boilerplate from dzgui.const.endpoints import STEAM_API_SETUP @@ -35,7 +35,7 @@ from dzgui.views.trees.tree_simple import SimpleTree import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gdk, Gtk, GLib, GObject, GdkPixbuf # noqa E402 +from gi.repository import Gtk, GLib, GObject, GdkPixbuf if TYPE_CHECKING: from dzgui.config.xdg import Xdg diff --git a/dzgui/views/mixins/context_mixin.py b/dzgui/views/mixins/context_mixin.py index 5befa2c..9a92739 100644 --- a/dzgui/views/mixins/context_mixin.py +++ b/dzgui/views/mixins/context_mixin.py @@ -9,7 +9,7 @@ from dzgui.views.trees.tree_base import TreeView import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa E402 +from gi.repository import Gtk, Gdk logger = logging.getLogger(APP_NAME) diff --git a/dzgui/views/mixins/cursor_mixin.py b/dzgui/views/mixins/cursor_mixin.py index 71d4851..0690e19 100644 --- a/dzgui/views/mixins/cursor_mixin.py +++ b/dzgui/views/mixins/cursor_mixin.py @@ -5,7 +5,7 @@ from dzgui.util.keys import is_ctrl_mask import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa E402 +from gi.repository import Gtk, Gdk class CursorMixin: diff --git a/dzgui/views/mixins/mods_mixin.py b/dzgui/views/mixins/mods_mixin.py index 6cc7da8..1b76321 100644 --- a/dzgui/views/mixins/mods_mixin.py +++ b/dzgui/views/mixins/mods_mixin.py @@ -3,7 +3,7 @@ from dzgui.views.trees.tree_base import TreeView import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa E402 +from gi.repository import Gtk class ModsMixin: diff --git a/dzgui/views/pages/changelog.py b/dzgui/views/pages/changelog.py index 108912c..86a7d32 100644 --- a/dzgui/views/pages/changelog.py +++ b/dzgui/views/pages/changelog.py @@ -15,7 +15,7 @@ from dzgui.views.mixins.scrollable_mixin import ScrollableMixin import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa E402 +from gi.repository import Gtk logger = logging.getLogger(APP_NAME) diff --git a/dzgui/views/pages/devs.py b/dzgui/views/pages/devs.py index 7c95255..3eccae8 100644 --- a/dzgui/views/pages/devs.py +++ b/dzgui/views/pages/devs.py @@ -7,10 +7,10 @@ from dzgui.util.strings import developers from dzgui.views.components.labels import BoldLabel from dzgui.views.trees.tree_base import TreeView -import gi # noqa E402 +import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa E402 +from gi.repository import Gtk if TYPE_CHECKING: from dzgui.controllers.mc import Controller diff --git a/dzgui/views/pages/help.py b/dzgui/views/pages/help.py index bcfbfd8..e4b1f17 100644 --- a/dzgui/views/pages/help.py +++ b/dzgui/views/pages/help.py @@ -4,7 +4,7 @@ from dzgui.views.trees.tree_menu import MenuTreeView import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa E402 +from gi.repository import Gtk if TYPE_CHECKING: from dzgui.controllers.mc import Controller diff --git a/dzgui/views/pages/keys.py b/dzgui/views/pages/keys.py index ea12476..92bea81 100644 --- a/dzgui/views/pages/keys.py +++ b/dzgui/views/pages/keys.py @@ -7,7 +7,7 @@ from dzgui.util.css import add_class import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa E402 +from gi.repository import Gtk if TYPE_CHECKING: from dzgui.controllers.mc import Controller diff --git a/dzgui/views/pages/log.py b/dzgui/views/pages/log.py index a9e1bd5..aad06a6 100644 --- a/dzgui/views/pages/log.py +++ b/dzgui/views/pages/log.py @@ -9,7 +9,7 @@ from dzgui.views.trees.tree_log import LogTreeView import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa E402 +from gi.repository import Gtk if TYPE_CHECKING: from dzgui.controllers.mc import Controller diff --git a/dzgui/views/pages/preconnect.py b/dzgui/views/pages/preconnect.py index b737ff0..686dc2f 100644 --- a/dzgui/views/pages/preconnect.py +++ b/dzgui/views/pages/preconnect.py @@ -18,7 +18,7 @@ from dzgui.views.trees.tree_server_mods import ServerModTreeView import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gdk, Gtk # noqa E402 +from gi.repository import Gdk, Gtk if TYPE_CHECKING: diff --git a/dzgui/views/pages/servers.py b/dzgui/views/pages/servers.py index caefe21..61e333b 100644 --- a/dzgui/views/pages/servers.py +++ b/dzgui/views/pages/servers.py @@ -10,7 +10,7 @@ from dzgui.util.strings import server_labels import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, Gdk, GLib # noqa E402 +from gi.repository import Gtk, Gdk logger = logging.getLogger(APP_NAME) diff --git a/dzgui/views/pages/thanks.py b/dzgui/views/pages/thanks.py index 4adc39c..88e4256 100644 --- a/dzgui/views/pages/thanks.py +++ b/dzgui/views/pages/thanks.py @@ -8,7 +8,7 @@ from dzgui.views.mixins.scrollable_mixin import ScrollableMixin import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa E402 +from gi.repository import Gtk # TODO: wrap and center long values if TYPE_CHECKING: diff --git a/dzgui/views/trees/tree_log.py b/dzgui/views/trees/tree_log.py index 7f141ef..fd4279d 100644 --- a/dzgui/views/trees/tree_log.py +++ b/dzgui/views/trees/tree_log.py @@ -12,7 +12,7 @@ from dzgui.views.mixins.context_mixin import ContextMixin import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa E402 +from gi.repository import Gtk, Gdk if TYPE_CHECKING: from pathlib import Path diff --git a/pyproject.toml b/pyproject.toml index 7d97ce6..d5780c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,6 +78,7 @@ line-length = 88 indent-width = 4 extend-exclude = ["*lib/a2s"] lint.unfixable = ["F401", "I001"] +ignore = ["E402"] [tool.ruff.format] quote-style = "double" diff --git a/tests/test_html_escaping.py b/tests/test_html_escaping.py index 904e5b2..2c3c03c 100644 --- a/tests/test_html_escaping.py +++ b/tests/test_html_escaping.py @@ -6,7 +6,7 @@ from dzgui.views.dialogs.servers import ServerDetailsDialog import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa E402 +from gi.repository import Gtk class MockController: From 9eb13428779b8d59aa149553bb40f8e3beb86f4f Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 03:00:35 +0900 Subject: [PATCH 09/34] drop: unused imports script --- scripts/unused_imports.py | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 scripts/unused_imports.py diff --git a/scripts/unused_imports.py b/scripts/unused_imports.py deleted file mode 100644 index 83498ad..0000000 --- a/scripts/unused_imports.py +++ /dev/null @@ -1,38 +0,0 @@ -from pathlib import Path - - -def iterate(search_str: str) -> None: - # TODO: exclude files from .gitignore - print() - print(f"Searching for '{search_str}'") - print() - files = Path(".").rglob("*") - ignore = [ - "scripts/", - ".git", - "egg", - "CHANGELOG", - ".mypy", - "build/", - "test.py", - "bugs", - "pyproject.toml", - ] - for file in files: - if file.is_dir(): - continue - if any(s in str(file) for s in ignore): - continue - try: - text = file.read_text() - if search_str in text: - c = text.count(search_str) - if 0 < c < 2: - print(file, c) - except Exception: - continue - - -imports = ["Pango", "GObject", "GLib", "Gdk", "Gtk"] -for i in imports: - iterate(i) From 13786591b39ba140c8c48158096b0715cb59a311 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 03:02:06 +0900 Subject: [PATCH 10/34] chore: restore exclusion for GLibUnix --- dzgui/views/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dzgui/views/base.py b/dzgui/views/base.py index 4ce0286..756dae2 100644 --- a/dzgui/views/base.py +++ b/dzgui/views/base.py @@ -38,7 +38,7 @@ import gi gi.require_version("Gtk", "3.0") gi.require_version("GLibUnix", "2.0") -from gi.repository import Gtk, GLib, GLibUnix, Gdk +from gi.repository import Gtk, GLib, GLibUnix, Gdk # type: ignore if TYPE_CHECKING: from dzgui.config.userprefs import UserPrefs From 3dc37561be8350433982ff8dda3bcdbf35f4d073 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 03:02:35 +0900 Subject: [PATCH 11/34] chore: update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d5780c0..9605b7b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,7 +78,7 @@ line-length = 88 indent-width = 4 extend-exclude = ["*lib/a2s"] lint.unfixable = ["F401", "I001"] -ignore = ["E402"] +lint.ignore = ["E402"] [tool.ruff.format] quote-style = "double" From 4e5fadf928c53d1a91e62ec3f6e5db9b1d479361 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 03:04:44 +0900 Subject: [PATCH 12/34] chore: drop old comments --- dzgui/app_init.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/dzgui/app_init.py b/dzgui/app_init.py index 1a942ab..178da23 100644 --- a/dzgui/app_init.py +++ b/dzgui/app_init.py @@ -158,9 +158,6 @@ def build_gui(version: str, is_debug: bool) -> App | None: f.truncate(0) if _is_steam_deck is False: - # TODO: sudo escalation dialog - # count = get_map_count() - # TODO: move into module if FLATPAK_ROOT_PATH in lookup(XDG.config, Preferences.DEFAULT): has_client = has_flatpak_client() else: From 598973104c6cb1e9d8dbe37ee075503c10c3f7de Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 04:30:40 +0900 Subject: [PATCH 13/34] change: reference dataclass values in enum --- dzgui/config/query.py | 3 +- dzgui/const/enum.py | 430 +++++++++++++++------------- dzgui/controllers/mc.py | 11 +- dzgui/managers/config.py | 2 +- dzgui/managers/contextmenu.py | 38 +-- dzgui/model/model_factory.py | 2 +- dzgui/util/strings.py | 2 + dzgui/views/components/buttonbox.py | 6 +- dzgui/views/components/crumbs.py | 2 +- dzgui/views/components/mod_panel.py | 4 +- dzgui/views/components/statusbar.py | 4 +- dzgui/views/mixins/context_mixin.py | 20 +- dzgui/views/trees/tree_menu.py | 2 +- dzgui/views/trees/tree_servers.py | 6 +- 14 files changed, 275 insertions(+), 257 deletions(-) diff --git a/dzgui/config/query.py b/dzgui/config/query.py index 1af14a5..e9d8a1f 100644 --- a/dzgui/config/query.py +++ b/dzgui/config/query.py @@ -5,6 +5,7 @@ from dzgui.util._json import read_json from dzgui.const.enum import Preferences + class ConfigFileError(Exception): pass @@ -39,4 +40,4 @@ def get_favorites(path: Path) -> list[str]: def enum_to_key(enum: Preferences) -> str: - return str(enum.dict["key"]) + return str(enum.key) diff --git a/dzgui/const/enum.py b/dzgui/const/enum.py index b1728f9..5b39564 100644 --- a/dzgui/const/enum.py +++ b/dzgui/const/enum.py @@ -1,7 +1,9 @@ +from __future__ import annotations +from dataclasses import dataclass from enum import Enum -from typing import Any, Self from dzgui.util import strings +from dzgui.strings import rows class Port(Enum): @@ -46,125 +48,61 @@ class FilterMode(Enum): TOGGLE_ON = 3 -class EnumWithAttrs(Enum): - def __new__(cls, *args: Any, **kwargs: Any) -> Self: - value = len(cls.__members__) + 1 - obj = object.__new__(cls) - obj._value_ = value - return obj - - def __init__(self, d: dict): - self.dict = d +@dataclass(slots=True, frozen=True) +class ModButtonMeta: + label: str + tooltip: str -class Preferences(EnumWithAttrs): - STEAM = { - "key": "steam_api", - } - CLIENT = { - "key": "client", - } - WINDOW = { - "key": "fullscreen", - } - DIST = { - "key": "use_miles", - } - NAME = { - "key": "name", - } - DEFAULT = { - "key": "default_steam_path", - } - FAV_LBL = { - "key": "fav_label", - } - FAV_SRV = { - "key": "fav_server", - } - IP_LIST = { - "key": "ip_list", - } - START_TAB = { - "key": "start_tab", - } +@dataclass(slots=True, frozen=True) +class ButtonTypeMeta: + label: str + tooltip: str + opens: NotebookPage -class NotebookPage(EnumWithAttrs): - OFFLINE = {"crumbs": strings.crumbs.offline, "statusbar": True} - CHANGELOG = {"crumbs": strings.crumbs.changelog, "statusbar": True} - DEVELOPERS = {"crumbs": strings.crumbs.developers, "statusbar": False} - HELP = {"crumbs": strings.crumbs._help, "statusbar": True} - KEYS = {"crumbs": strings.crumbs.keys, "statusbar": True} - LOG = {"crumbs": strings.crumbs.log, "statusbar": True} - MODS = {"crumbs": strings.crumbs.mods, "statusbar": True} - OPTIONS = {"crumbs": strings.crumbs.options, "statusbar": False} - SERVERS = {"crumbs": strings.crumbs.servers, "statusbar": True} - THANKS = {"crumbs": strings.crumbs.thanks, "statusbar": True} - CONNECTION = {"crumbs": "Connect", "statusbar": True} +@dataclass(slots=True, frozen=True) +class ConfigKey: + key: str -class RowType(EnumWithAttrs): - # TODO: strings - CHANGELOG = { - "label": "View changelog", - "tooltip": "Opens the DZGUI changelog", - } - SHOW_LOG = { - "label": "Show debug log", - "tooltip": "Read the DZGUI log generated since startup", - } - SEPARATOR = { - "label": "SEPARATOR", - "tooltip": "", - } - DUMP_LOG = { - "label": "Output system info to log file", - "tooltip": "Dump diagnostic data for troubleshooting", - } - DOCS = { - "label": "Documentation/help files (GitHub) ⧉", - "tooltip": "Open the DZGUI documentation in a browser", - } - DOCS_FALLBACK = { - "label": "Documentation/help files (Codeberg mirror) ⧉", - "tooltip": "Open the DZGUI documentation in a browser", - } - BUGS = { - "label": "Report a bug (GitHub) ⧉", - "tooltip": "Open the DZGUI issue tracker in a browser", - } - FORUM = { - "label": "DZGUI Subreddit ⧉", - "tooltip": "Open the DZGUI discussion forum in a browser", - } - SPONSOR = { - "label": "Sponsor (GitHub) ⧉", - "tooltip": "Sponsor development of the DZGUI project", - } - THANKS = { - "label": "Special thanks", - "tooltip": "A list of contributors, testers, and sponsors", - } +@dataclass(slots=True, frozen=True) +class NotebookMeta: + crumbs: str + has_statusbar: bool -# TODO: rename to ContextMenuItem -class ContextMenu(EnumWithAttrs): - ADD_NOTE = {"label": strings.add_note} - ADD_SERVER = {"label": strings.add} - CONNECT = {"label": strings.connect} - COPY_LOG_CLIPBOARD = {"label": strings.copy_log} - COPY_SERVER_IP = {"label": strings.copy_ip} - COPY_SERVER_NAME = {"label": strings.copy_name} - UNSUB_MOD = {"label": strings.unsub_mod} - VALIDATE_MOD = {"label": strings.validate_mod} - OPEN_WORKSHOP = {"label": strings.open_workshop} - REFRESH_PLAYERS = {"label": strings.refresh_players} - REMOVE_HISTORY = {"label": strings.remove_history} - REMOVE_SERVER = {"label": strings.remove} - SET_FAV = {"label": strings.add_fav} - SHOW_DETAILS = {"label": strings.show_details} - SHOW_MODS = {"label": strings.show_mods} +@dataclass(slots=True, frozen=True) +class RowMeta: + label: str + tooltip: str + + +@dataclass(slots=True, frozen=True) +class ContextMenuMeta: + label: str + + +class ContextMenuItem(Enum): + ADD_NOTE = ContextMenuMeta(strings.add_note) + ADD_SERVER = ContextMenuMeta(strings.add) + CONNECT = ContextMenuMeta(strings.connect) + COPY_LOG_CLIPBOARD = ContextMenuMeta(strings.copy_log) + COPY_SERVER_IP = ContextMenuMeta(strings.copy_ip) + COPY_SERVER_NAME = ContextMenuMeta(strings.copy_name) + UNSUB_MOD = ContextMenuMeta(strings.unsub_mod) + VALIDATE_MOD = ContextMenuMeta(strings.validate_mod) + OPEN_WORKSHOP = ContextMenuMeta(strings.open_workshop) + REFRESH_PLAYERS = ContextMenuMeta(strings.refresh_players) + REMOVE_HISTORY = ContextMenuMeta(strings.remove_history) + REMOVE_SERVER = ContextMenuMeta(strings.remove) + SET_FAV = ContextMenuMeta(strings.add_fav) + SHOW_DETAILS = ContextMenuMeta(strings.show_details) + SHOW_MODS = ContextMenuMeta(strings.show_mods) + + @property + def label(self) -> str: + return self.value.label class ContextMenuGroup(Enum): @@ -172,113 +110,193 @@ class ContextMenuGroup(Enum): Groupings of context menu items """ - SERVER_MOD = (ContextMenu.OPEN_WORKSHOP,) - MOD = (ContextMenu.OPEN_WORKSHOP, ContextMenu.UNSUB_MOD, ContextMenu.VALIDATE_MOD) + SERVER_MOD = (ContextMenuItem.OPEN_WORKSHOP,) + MOD = ( + ContextMenuItem.OPEN_WORKSHOP, + ContextMenuItem.UNSUB_MOD, + ContextMenuItem.VALIDATE_MOD, + ) MOD_OFFLINE = (None,) - LOG = (ContextMenu.COPY_LOG_CLIPBOARD,) + LOG = (ContextMenuItem.COPY_LOG_CLIPBOARD,) SERVER_BROWSER = ( - ContextMenu.CONNECT, - ContextMenu.ADD_SERVER, - ContextMenu.SET_FAV, - ContextMenu.COPY_SERVER_NAME, - ContextMenu.COPY_SERVER_IP, - ContextMenu.ADD_NOTE, - ContextMenu.SHOW_MODS, - ContextMenu.SHOW_DETAILS, - ContextMenu.REFRESH_PLAYERS, + ContextMenuItem.CONNECT, + ContextMenuItem.ADD_SERVER, + ContextMenuItem.SET_FAV, + ContextMenuItem.COPY_SERVER_NAME, + ContextMenuItem.COPY_SERVER_IP, + ContextMenuItem.ADD_NOTE, + ContextMenuItem.SHOW_MODS, + ContextMenuItem.SHOW_DETAILS, + ContextMenuItem.REFRESH_PLAYERS, ) SCAN_LAN = ( - ContextMenu.CONNECT, - ContextMenu.SET_FAV, - ContextMenu.COPY_SERVER_NAME, - ContextMenu.COPY_SERVER_IP, - ContextMenu.ADD_NOTE, - ContextMenu.SHOW_MODS, - ContextMenu.SHOW_DETAILS, - ContextMenu.REFRESH_PLAYERS, + ContextMenuItem.CONNECT, + ContextMenuItem.SET_FAV, + ContextMenuItem.COPY_SERVER_NAME, + ContextMenuItem.COPY_SERVER_IP, + ContextMenuItem.ADD_NOTE, + ContextMenuItem.SHOW_MODS, + ContextMenuItem.SHOW_DETAILS, + ContextMenuItem.REFRESH_PLAYERS, ) SAVED = ( - ContextMenu.CONNECT, - ContextMenu.SET_FAV, - ContextMenu.REMOVE_SERVER, - ContextMenu.COPY_SERVER_NAME, - ContextMenu.COPY_SERVER_IP, - ContextMenu.ADD_NOTE, - ContextMenu.SHOW_MODS, - ContextMenu.SHOW_DETAILS, - ContextMenu.REFRESH_PLAYERS, + ContextMenuItem.CONNECT, + ContextMenuItem.SET_FAV, + ContextMenuItem.REMOVE_SERVER, + ContextMenuItem.COPY_SERVER_NAME, + ContextMenuItem.COPY_SERVER_IP, + ContextMenuItem.ADD_NOTE, + ContextMenuItem.SHOW_MODS, + ContextMenuItem.SHOW_DETAILS, + ContextMenuItem.REFRESH_PLAYERS, ) RECENT = ( - ContextMenu.CONNECT, - ContextMenu.ADD_SERVER, - ContextMenu.SET_FAV, - ContextMenu.REMOVE_HISTORY, - ContextMenu.COPY_SERVER_NAME, - ContextMenu.COPY_SERVER_IP, - ContextMenu.ADD_NOTE, - ContextMenu.SHOW_MODS, - ContextMenu.SHOW_DETAILS, - ContextMenu.REFRESH_PLAYERS, + ContextMenuItem.CONNECT, + ContextMenuItem.ADD_SERVER, + ContextMenuItem.SET_FAV, + ContextMenuItem.REMOVE_HISTORY, + ContextMenuItem.COPY_SERVER_NAME, + ContextMenuItem.COPY_SERVER_IP, + ContextMenuItem.ADD_NOTE, + ContextMenuItem.SHOW_MODS, + ContextMenuItem.SHOW_DETAILS, + ContextMenuItem.REFRESH_PLAYERS, ) -class ModButton(EnumWithAttrs): - SELECT_ALL = { - "label": strings.mod_panel.select_all, - "tooltip": strings.mod_panel.select_all_tooltip, - } - UNSELECT_ALL = { - "label": strings.mod_panel.unselect_all, - "tooltip": strings.mod_panel.unselect_all_tooltip, - } - HIGHLIGHT_STALE = { - "label": strings.mod_panel.highlight_stale, - "tooltip": strings.mod_panel.highlight_stale_tooltip, - } - UNHIGHLIGHT_STALE = { - "label": strings.mod_panel.unhighlight_stale, - "tooltip": strings.mod_panel.unhighlight_stale_tooltip, - } - UNSUB_SELECTED = { - "label": strings.mod_panel.unsub_selected, - "tooltip": strings.mod_panel.unsub_selected_tooltip, - } - SELECT_STALE = { - "label": strings.mod_panel.select_stale, - "tooltip": strings.mod_panel.select_stale_tooltip, - } - VALIDATE = { - "label": strings.mod_panel.validate, - "tooltip": strings.mod_panel.validate_tooltip, - } +class Preferences(Enum): + STEAM = ConfigKey("steam_api") + CLIENT = ConfigKey("client") + WINDOW = ConfigKey("fullscreen") + DIST = ConfigKey("use_miles") + NAME = ConfigKey("name") + DEFAULT = ConfigKey("default_steam_path") + FAV_LBL = ConfigKey("fav_label") + FAV_SRV = ConfigKey("fav_server") + IP_LIST = ConfigKey("ip_list") + START_TAB = ConfigKey("start_tab") + + @property + def key(self) -> str: + return self.value.key -class ButtonType(EnumWithAttrs): - SERVERS = { - "label": strings.buttons.servers_label, - "tooltip": strings.buttons.servers_tooltip, - "opens": NotebookPage.SERVERS, - } - MODS = { - "label": strings.buttons.mods_label, - "tooltip": strings.buttons.mods_tooltip, - "opens": NotebookPage.MODS, - } - OPTIONS = { - "label": strings.buttons.options_label, - "tooltip": strings.buttons.options_tooltip, - "opens": NotebookPage.OPTIONS, - } - HELP = { - "label": strings.buttons.help_label, - "tooltip": strings.buttons.help_tooltip, - "opens": NotebookPage.HELP, - } - EXIT = { - "label": strings.buttons.exit_label, - "tooltip": strings.buttons.exit_tooltip, - "opens": None, - } +class NotebookPage(Enum): + OFFLINE = NotebookMeta(strings.crumbs.offline, True) + CHANGELOG = NotebookMeta(strings.crumbs.changelog, True) + DEVELOPERS = NotebookMeta(strings.crumbs.developers, False) + HELP = NotebookMeta(strings.crumbs._help, True) + KEYS = NotebookMeta(strings.crumbs.keys, True) + LOG = NotebookMeta(strings.crumbs.log, True) + MODS = NotebookMeta(strings.crumbs.mods, True) + OPTIONS = NotebookMeta(strings.crumbs.options, False) + SERVERS = NotebookMeta(strings.crumbs.servers, True) + THANKS = NotebookMeta(strings.crumbs.thanks, True) + CONNECTION = NotebookMeta(strings.crumbs.connect, True) + + @property + def crumbs(self) -> str: + return self.value.crumbs + + @property + def has_statusbar(self) -> bool: + return self.value.has_statusbar + + +class RowType(Enum): + CHANGELOG = RowMeta(rows.changelog_label, rows.changelog_status) + SHOW_LOG = RowMeta(rows.log_label, rows.log_status) + SEPARATOR = RowMeta(rows.separator_label, rows.separator_status) + DUMP_LOG = RowMeta(rows.system_log_label, rows.system_log_status) + DOCS = RowMeta(rows.docs_label, rows.docs_status) + DOCS_FALLBACK = RowMeta(rows.docs_fallback_label, rows.docs_fallback_status) + BUGS = RowMeta(rows.bugs_label, rows.bugs_status) + FORUM = RowMeta(rows.forum_label, rows.forum_status) + SPONSOR = RowMeta(rows.sponsor_label, rows.sponsor_status) + THANKS = RowMeta(rows.thanks_label, rows.thanks_status) + + @property + def label(self) -> str: + return self.value.label + + @property + def tooltip(self) -> str: + return self.value.tooltip + + +class ModButton(Enum): + SELECT_ALL = ModButtonMeta( + strings.mod_panel.select_all, strings.mod_panel.select_all_tooltip + ) + UNSELECT_ALL = ModButtonMeta( + strings.mod_panel.unselect_all, strings.mod_panel.unselect_all_tooltip + ) + HIGHLIGHT_STALE = ModButtonMeta( + strings.mod_panel.highlight_stale, strings.mod_panel.highlight_stale_tooltip + ) + UNHIGHLIGHT_STALE = ModButtonMeta( + strings.mod_panel.unhighlight_stale, strings.mod_panel.unhighlight_stale_tooltip + ) + UNSUB_SELECTED = ModButtonMeta( + strings.mod_panel.unsub_selected, + strings.mod_panel.unsub_selected_tooltip, + ) + SELECT_STALE = ModButtonMeta( + strings.mod_panel.select_stale, + strings.mod_panel.select_stale_tooltip, + ) + VALIDATE = ModButtonMeta( + strings.mod_panel.validate, + strings.mod_panel.validate_tooltip, + ) + + @property + def label(self) -> str: + return self.value.label + + @property + def tooltip(self) -> str: + return self.value.tooltip + + +class ButtonType(Enum): + SERVERS = ButtonTypeMeta( + strings.buttons.servers_label, + strings.buttons.servers_tooltip, + NotebookPage.SERVERS, + ) + MODS = ButtonTypeMeta( + strings.buttons.mods_label, + strings.buttons.mods_tooltip, + NotebookPage.MODS, + ) + OPTIONS = ButtonTypeMeta( + strings.buttons.options_label, + strings.buttons.options_tooltip, + NotebookPage.OPTIONS, + ) + HELP = ButtonTypeMeta( + strings.buttons.help_label, + strings.buttons.help_tooltip, + NotebookPage.HELP, + ) + EXIT = ButtonTypeMeta( + strings.buttons.exit_label, + strings.buttons.exit_tooltip, + None, + ) + + @property + def label(self) -> str: + return self.value.label + + @property + def tooltip(self) -> str: + return self.value.tooltip + + @property + def opens(self) -> str: + return self.value.opens HELP_MENU_ROWS = ( diff --git a/dzgui/controllers/mc.py b/dzgui/controllers/mc.py index 1a05955..192b327 100644 --- a/dzgui/controllers/mc.py +++ b/dzgui/controllers/mc.py @@ -8,7 +8,7 @@ import dzgui.util._json as JSON # noqa from dzgui.const.enum import ( ButtonType, - ContextMenu, + ContextMenuItem, FilterMode, NotebookPage, Preferences, @@ -262,7 +262,7 @@ class Controller(GObject.GObject): logger.critical(e) return - def menu_action(self, action: ContextMenu, tree: "ServerTreeView") -> None: + def menu_action(self, action: ContextMenuItem, tree: "ServerTreeView") -> None: context_man = ContextMenuManager(tree, self) context_man.process(action) @@ -274,7 +274,7 @@ class Controller(GObject.GObject): except Exception as e: dialog = ExceptionDialog(self, str(e)) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def select_colorized(self) -> None: mod_man = self.mediator.modtreeview.get_mod_man() @@ -308,7 +308,7 @@ class Controller(GObject.GObject): except Exception as e: dialog = ExceptionDialog(self, str(e)) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def update_steam_api_key(self, text: str) -> None: self.config_man.update_steam_api_key(text) @@ -365,12 +365,11 @@ class Controller(GObject.GObject): filter_man = self.get_filter_man() return filter_man.get_map_store() - def get_enabled_filters(self) -> dict: + def get_enabled_filters(self) -> dict[str, bool]: filter_man = self.get_filter_man() return filter_man.get_filters() # FIXME: wipe maps store when changing tabs if model is none - def has_server_model(self) -> bool: treeview = self.get_active_treeview() proxy_man = treeview.get_proxy_man() diff --git a/dzgui/managers/config.py b/dzgui/managers/config.py index 7490b15..8b715ec 100644 --- a/dzgui/managers/config.py +++ b/dzgui/managers/config.py @@ -124,7 +124,7 @@ class ConfigManager: return False def enum_to_key(self, enum: Preferences) -> str: - return str(enum.dict["key"]) + return str(enum.key) def toggle_config(self, key: Preferences) -> None: try: diff --git a/dzgui/managers/contextmenu.py b/dzgui/managers/contextmenu.py index 016ce7b..ded03ec 100644 --- a/dzgui/managers/contextmenu.py +++ b/dzgui/managers/contextmenu.py @@ -2,7 +2,7 @@ import logging from typing import TYPE_CHECKING from dzgui.const.constants import APP_NAME -from dzgui.const.enum import ContextMenu, Preferences +from dzgui.const.enum import ContextMenuItem, Preferences from dzgui.util.clip import copy_clipboard from dzgui.util.open_links import open_workshop_page @@ -34,7 +34,7 @@ class ContextMenuManager: self.controller = controller self.treeview = treeview - def process(self, action: ContextMenu) -> None: + def process(self, action: ContextMenuItem) -> None: if isinstance(self.treeview, ServerTreeView): record = self.treeview.get_record() if record is None: @@ -42,14 +42,14 @@ class ContextMenuManager: match action: # UNTHREADED - case ContextMenu.ADD_NOTE: + case ContextMenuItem.ADD_NOTE: dialog = NoteDialog(self.controller) - dialog.run() - case ContextMenu.COPY_SERVER_IP: + dialog.run() # type: ignore[no-untyped-call] + case ContextMenuItem.COPY_SERVER_IP: self.copy_server_ip() - case ContextMenu.COPY_SERVER_NAME: + case ContextMenuItem.COPY_SERVER_NAME: self.copy_server_name() - case ContextMenu.SET_FAV: + case ContextMenuItem.SET_FAV: name = self.treeview.get_value_at_index(0) # NOTE: fully qualified ip fqip = self.treeview.get_record_string() @@ -58,40 +58,40 @@ class ContextMenuManager: self.controller.set_fav(name, fqip, simple) # THREADED - case ContextMenu.ADD_SERVER: + case ContextMenuItem.ADD_SERVER: self.controller.add_by_record(record) - case ContextMenu.CONNECT: + case ContextMenuItem.CONNECT: self.controller.connect_by_record(record) - case ContextMenu.REFRESH_PLAYERS: + case ContextMenuItem.REFRESH_PLAYERS: self.controller.refresh_players(record) - case ContextMenu.REMOVE_HISTORY: + case ContextMenuItem.REMOVE_HISTORY: self.controller.remove_from_history(record) - case ContextMenu.REMOVE_SERVER: + case ContextMenuItem.REMOVE_SERVER: self.controller.remove_by_record(record) - case ContextMenu.SHOW_DETAILS: + case ContextMenuItem.SHOW_DETAILS: self.controller.get_details(record) - case ContextMenu.SHOW_MODS: + case ContextMenuItem.SHOW_MODS: self.controller.get_modlist(record) if isinstance(self.treeview, (ModTreeView, OfflineModTreeView)): match action: - case ContextMenu.OPEN_WORKSHOP: + case ContextMenuItem.OPEN_WORKSHOP: self.open_mod_page() - case ContextMenu.UNSUB_MOD: + case ContextMenuItem.UNSUB_MOD: self.controller.unsub_mods(self.treeview) - case ContextMenu.VALIDATE_MOD: + case ContextMenuItem.VALIDATE_MOD: self.controller.validate_mods(self.treeview) case _: pass if isinstance(self.treeview, LogTreeView): match action: - case ContextMenu.COPY_LOG_CLIPBOARD: + case ContextMenuItem.COPY_LOG_CLIPBOARD: self.copy_log() if isinstance(self.treeview, ServerModTreeView): match action: - case ContextMenu.OPEN_WORKSHOP: + case ContextMenuItem.OPEN_WORKSHOP: self.open_mod_page() def copy_server_ip(self) -> None: diff --git a/dzgui/model/model_factory.py b/dzgui/model/model_factory.py index 7dfffbf..cee089f 100644 --- a/dzgui/model/model_factory.py +++ b/dzgui/model/model_factory.py @@ -147,7 +147,7 @@ class ModelFactory: def make_help_store(self) -> FastInsertListStore: store = self.new_model_from_class(MenuCols) - rows = [[row.dict["label"], row] for row in HELP_MENU_ROWS] + rows = [[row.label, row] for row in HELP_MENU_ROWS] store.extend(rows) return store diff --git a/dzgui/util/strings.py b/dzgui/util/strings.py index 931ca88..ac27a7a 100644 --- a/dzgui/util/strings.py +++ b/dzgui/util/strings.py @@ -252,6 +252,7 @@ class Crumbs: developers: str default: str offline: str + connect: str @dataclass(slots=True, frozen=True) @@ -439,6 +440,7 @@ crumbs = Crumbs( developers="Options > Developers", default="Servers > ", offline="Mods > Play offline", + connect="Connect", ) no_mods = "No local mods found." diff --git a/dzgui/views/components/buttonbox.py b/dzgui/views/components/buttonbox.py index 496f237..2b440cf 100644 --- a/dzgui/views/components/buttonbox.py +++ b/dzgui/views/components/buttonbox.py @@ -51,9 +51,9 @@ class ButtonBox(Gtk.Box): for side_button in ButtonType: button = ContextualButton( - label=side_button.dict["label"], - opens=side_button.dict["opens"], - tooltip=side_button.dict["tooltip"], + label=side_button.label, + opens=side_button.opens, + tooltip=side_button.tooltip, context=side_button, ) diff --git a/dzgui/views/components/crumbs.py b/dzgui/views/components/crumbs.py index cd57426..7366d5e 100644 --- a/dzgui/views/components/crumbs.py +++ b/dzgui/views/components/crumbs.py @@ -43,7 +43,7 @@ class Breadcrumbs(Gtk.Label): label = page.get_current_tab_text() self.set_server_crumbs(label) else: - crumbs = enum.dict["crumbs"] + crumbs = enum.crumbs self.set_crumbs(crumbs) def set_server_crumbs(self, suffix: str) -> None: diff --git a/dzgui/views/components/mod_panel.py b/dzgui/views/components/mod_panel.py index 7c913c4..c7693d9 100644 --- a/dzgui/views/components/mod_panel.py +++ b/dzgui/views/components/mod_panel.py @@ -20,8 +20,8 @@ class EnumeratedModButton(Gtk.Button): super().__init__(margin_start=10, margin_end=10, focus_on_click=False) self.enum = enum - self.set_label(enum.dict["label"]) - self.set_tooltip_text(enum.dict["tooltip"]) + self.set_label(enum.label) + self.set_tooltip_text(enum.tooltip) class ModPanelButton(EnumeratedModButton): diff --git a/dzgui/views/components/statusbar.py b/dzgui/views/components/statusbar.py index 7eb75a3..b6cb1fc 100644 --- a/dzgui/views/components/statusbar.py +++ b/dzgui/views/components/statusbar.py @@ -60,7 +60,7 @@ class Statusbar(Gtk.Grid): return enum = notebook.get_page_by_enum() - show_statusbar = enum.dict["statusbar"] + show_statusbar = enum.has_statusbar if show_statusbar is False: self.set_by_context(enum, "") @@ -152,5 +152,5 @@ class Statusbar(Gtk.Grid): row = tree.get_value_at_index(1) if self.controller.loaded is False: return - tooltip = row.dict["tooltip"] + tooltip = row.tooltip self.set_by_context(NotebookPage.HELP, tooltip) diff --git a/dzgui/views/mixins/context_mixin.py b/dzgui/views/mixins/context_mixin.py index 5befa2c..2742f89 100644 --- a/dzgui/views/mixins/context_mixin.py +++ b/dzgui/views/mixins/context_mixin.py @@ -1,7 +1,7 @@ import logging from dzgui.const.constants import APP_NAME -from dzgui.const.enum import ContextMenu +from dzgui.const.enum import ContextMenuItem from dzgui.util.keys import is_ctrl_mask, is_navkey from dzgui.util.strings import edit_note from dzgui.views.trees.tree_base import TreeView @@ -15,9 +15,7 @@ logger = logging.getLogger(APP_NAME) class ContextMixin(TreeView): - def present_menu( - self, widget: Gtk.Widget, event: Gdk.Event - ) -> bool: + def present_menu(self, widget: Gtk.Widget, event: Gdk.Event) -> bool: if self.menu is None: return False @@ -60,17 +58,17 @@ class ContextMixin(TreeView): self.context_menu.select_first(False) return True - def _process_dynamic_row(self, row: ContextMenu) -> Gtk.MenuItem: - if row == ContextMenu.ADD_SERVER and self.is_in_favs(): # type: ignore - row = ContextMenu.REMOVE_SERVER + def _process_dynamic_row(self, row: ContextMenuItem) -> Gtk.MenuItem: + if row == ContextMenuItem.ADD_SERVER and self.is_in_favs(): # type: ignore + row = ContextMenuItem.REMOVE_SERVER - item = Gtk.MenuItem(label=row.dict["label"]) + item = Gtk.MenuItem(label=row.label) item.connect("activate", self._on_menu_click, row) - if row == ContextMenu.SHOW_MODS: + if row == ContextMenuItem.SHOW_MODS: item.set_sensitive(self.is_modded()) # type: ignore - if row == ContextMenu.ADD_NOTE: + if row == ContextMenuItem.ADD_NOTE: if self.controller.has_note(): item.set_label(edit_note) @@ -94,7 +92,7 @@ class ContextMixin(TreeView): except AttributeError: return False - def _on_menu_click(self, widget: Gtk.MenuItem, enum: ContextMenu) -> None: + def _on_menu_click(self, widget: Gtk.MenuItem, enum: ContextMenuItem) -> None: self.controller.menu_action(enum, self) # type: ignore def _on_key(self, menu: Gtk.Menu, event: Gdk.EventKey) -> bool | None: diff --git a/dzgui/views/trees/tree_menu.py b/dzgui/views/trees/tree_menu.py index 04a17f2..a0964bc 100644 --- a/dzgui/views/trees/tree_menu.py +++ b/dzgui/views/trees/tree_menu.py @@ -77,4 +77,4 @@ class MenuTreeView(TreeView): raise ValueError("Trying to call a method on non-existent model") _iter = self.get_focused_row_iter() rowtype = model.get_value(_iter, 1) - return str(rowtype.dict["tooltip"]) + return str(rowtype.tooltip) diff --git a/dzgui/views/trees/tree_servers.py b/dzgui/views/trees/tree_servers.py index 3143c6d..9dd19ff 100644 --- a/dzgui/views/trees/tree_servers.py +++ b/dzgui/views/trees/tree_servers.py @@ -7,7 +7,7 @@ from warnings import deprecated from dzgui.api.servers import ping, Record from dzgui.const.constants import APP_NAME -from dzgui.const.enum import ContextMenu, ContextMenuGroup, ServerTab +from dzgui.const.enum import ContextMenuItem, ContextMenuGroup, ServerTab from dzgui.managers.filter import FilterManager from dzgui.model.proxy_model import ProxyModelManager from dzgui.util import strings @@ -250,9 +250,9 @@ class ServerTreeView(ContextMixin, TreeView): # type: ignore if self.enum is ServerTab.LAN: self.emitter.emit("request_custom_port_focus") case Gdk.KEY_c: - self.controller.menu_action(ContextMenu.COPY_SERVER_IP, self) + self.controller.menu_action(ContextMenuItem.COPY_SERVER_IP, self) case Gdk.KEY_r: - self.controller.menu_action(ContextMenu.REFRESH_PLAYERS, self) + self.controller.menu_action(ContextMenuItem.REFRESH_PLAYERS, self) else: match event.keyval: case Gdk.KEY_l | Gdk.KEY_Right: From 8da264686ab1231d12ec5834a88f3c75626d0ba9 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 04:33:54 +0900 Subject: [PATCH 14/34] chore: clear typehinting errors --- dzgui/const/enum.py | 4 ++-- dzgui/controllers/mc.py | 4 +++- dzgui/views/components/buttonbox.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/dzgui/const/enum.py b/dzgui/const/enum.py index 5b39564..d7c8911 100644 --- a/dzgui/const/enum.py +++ b/dzgui/const/enum.py @@ -58,7 +58,7 @@ class ModButtonMeta: class ButtonTypeMeta: label: str tooltip: str - opens: NotebookPage + opens: NotebookPage | None @dataclass(slots=True, frozen=True) @@ -295,7 +295,7 @@ class ButtonType(Enum): return self.value.tooltip @property - def opens(self) -> str: + def opens(self) -> NotebookPage | None: return self.value.opens diff --git a/dzgui/controllers/mc.py b/dzgui/controllers/mc.py index 192b327..9116039 100644 --- a/dzgui/controllers/mc.py +++ b/dzgui/controllers/mc.py @@ -196,7 +196,9 @@ class Controller(GObject.GObject): def show_developers_page(self) -> None: self.open_page(NotebookPage.DEVELOPERS) - def open_page(self, page: NotebookPage) -> None: + def open_page(self, page: NotebookPage | None) -> None: + if page is None: + return self.mediator.grid.notebook.set_page_by_enum(page) def open_page_by_button(self, button: "ContextualButton") -> None: diff --git a/dzgui/views/components/buttonbox.py b/dzgui/views/components/buttonbox.py index 2b440cf..3f36c8b 100644 --- a/dzgui/views/components/buttonbox.py +++ b/dzgui/views/components/buttonbox.py @@ -19,7 +19,7 @@ if TYPE_CHECKING: class ContextualButton(Gtk.Button): def __init__( - self, label: str, opens: "NotebookPage", tooltip: str, context: ButtonType + self, label: str, opens: "NotebookPage" | None, tooltip: str, context: ButtonType ) -> None: super().__init__( label=label, From c94e4e73db99e2c68170aba5435fe475b75cb648 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 04:34:38 +0900 Subject: [PATCH 15/34] chore: add strings file --- dzgui/strings/rows.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 dzgui/strings/rows.py diff --git a/dzgui/strings/rows.py b/dzgui/strings/rows.py new file mode 100644 index 0000000..636dd7f --- /dev/null +++ b/dzgui/strings/rows.py @@ -0,0 +1,29 @@ +changelog_label = "View changelog" +changelog_status = "Opens the DZGUI changelog" + +log_label = "Show debug log" +log_status = "Read the DZGUI log generated since startup" + +separator_label = "SEPARATOR" +separator_status = "" + +system_log_label = "Output system info to log file" +system_log_status = "Dump diagnostic data for troubleshooting" + +docs_label = "Documentation/help files (GitHub) ⧉" +docs_status = "Open the DZGUI documentation in a browser" + +docs_fallback_label = "Documentation/help files (Codeberg mirror) ⧉" +docs_fallback_status = "Open the DZGUI documentation in a browser" + +bugs_label = "Report a bug (GitHub) ⧉" +bugs_status = "Open the DZGUI issue tracker in a browser" + +forum_label = "DZGUI Subreddit ⧉" +forum_status = "Open the DZGUI discussion forum in a browser" + +sponsor_label = "Sponsor (GitHub) ⧉" +sponsor_status = "Sponsor development of the DZGUI project" + +thanks_label = "Special thanks" +thanks_status = "A list of contributors, testers, and sponsors" From f092207c6113438a849aadc2bbf50f013179e93f Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 04:37:51 +0900 Subject: [PATCH 16/34] chore: clear strict typehinting errors --- dzgui/managers/config.py | 2 +- dzgui/managers/connection.py | 8 ++++---- dzgui/managers/filter.py | 2 +- dzgui/managers/mods.py | 2 +- dzgui/managers/offline.py | 2 +- dzgui/managers/threading.py | 6 +++--- dzgui/managers/update.py | 4 ++-- dzgui/model/servers.py | 6 +++--- dzgui/views/components/filter_panel.py | 6 +++++- dzgui/views/dialogs/wizard.py | 9 +++++++-- dzgui/views/pages/options.py | 8 ++++---- dzgui/views/pages/preconnect.py | 2 +- dzgui/views/trees/tree_servers.py | 2 +- 13 files changed, 34 insertions(+), 25 deletions(-) diff --git a/dzgui/managers/config.py b/dzgui/managers/config.py index 7490b15..de5d12b 100644 --- a/dzgui/managers/config.py +++ b/dzgui/managers/config.py @@ -162,7 +162,7 @@ class ConfigManager: trace = traceback.format_exc() dialog = ExceptionDialog(self.controller, trace) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def save_res_and_quit(self, tv: "ServerTreeView", window: "OuterWindow") -> None: columns = tv.get_columns() diff --git a/dzgui/managers/connection.py b/dzgui/managers/connection.py index 89c35f0..80291fa 100644 --- a/dzgui/managers/connection.py +++ b/dzgui/managers/connection.py @@ -298,22 +298,22 @@ class ConnectionManager: def _present_modlist_dialog(self, mods: list[list[str]]) -> None: dialog = ServerModDialog(self.controller, mods) - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def _present_details_dialog(self, details: Servers.Details) -> None: dialog = ServerDetailsDialog(self.controller, details) - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def _workshop_timeout(self, msg: str) -> None: dialog = ExceptionDialog(self.controller, msg) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def _server_timeout(self) -> None: dialog = ExceptionDialog(self.controller, server_timeout) dialog.set_details_buffer(kb.DZG_006) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def get_debug_args(self) -> str: addr = f"{self.record.ip}:{self.record.gameport}" diff --git a/dzgui/managers/filter.py b/dzgui/managers/filter.py index 4151bc5..f3f175c 100644 --- a/dzgui/managers/filter.py +++ b/dzgui/managers/filter.py @@ -79,7 +79,7 @@ class FilterManager: """Deep copy of defaults""" return dict(self.default_filters) - def get_filters(self) -> dict: + def get_filters(self) -> dict[str, bool]: return self.enabled_filters def set_filter(self, label: str, state: bool) -> None: diff --git a/dzgui/managers/mods.py b/dzgui/managers/mods.py index 6a98e02..b94a1c5 100644 --- a/dzgui/managers/mods.py +++ b/dzgui/managers/mods.py @@ -221,7 +221,7 @@ class ModManager: total_size += mod[3] return total_mods, total_size - def _on_stale_mods_found(self, stale_mods: list) -> None: + def _on_stale_mods_found(self, stale_mods: list[int]) -> None: """Manipulates attached ListStore in the main event loop""" model = self.treeview.get_model() if model is None: diff --git a/dzgui/managers/offline.py b/dzgui/managers/offline.py index 645a682..d16acaa 100644 --- a/dzgui/managers/offline.py +++ b/dzgui/managers/offline.py @@ -52,7 +52,7 @@ class OfflineManager: is_valid = is_mission(folder) self.emitter.emit("custom_mission_loaded", str(folder), is_valid) - def find_custom_mods(self, callback: Callable) -> None: + def find_custom_mods(self, callback: Callable[[], None]) -> None: folder = self.open_folderpicker(dialogs.custom_mod_dialog) window = self.controller.get_window() window.set_sensitive(False) diff --git a/dzgui/managers/threading.py b/dzgui/managers/threading.py index c20f324..023f017 100644 --- a/dzgui/managers/threading.py +++ b/dzgui/managers/threading.py @@ -22,8 +22,8 @@ logger = logging.getLogger(APP_NAME) def call_on_thread( dialog_str: str, show_dialog: bool = True, show_cancel: bool = False -) -> Callable: - def decorator(func: Callable) -> Callable: +) -> Callable[..., Any]: + def decorator(func: Callable[..., Any]) -> Callable[..., Any]: @wraps(func) def wrapper(*args: Any, **kwargs: Any) -> None: self = args[0] @@ -42,7 +42,7 @@ def call_on_thread( class StoredFunc: - def __init__(self, func: Callable, *args: Any, **kwargs: Any) -> None: + def __init__(self, func: Callable[..., Any], *args: Any, **kwargs: Any) -> None: sig = inspect.signature(func) self.func = func self.bindings = sig.bind(*args, **kwargs) diff --git a/dzgui/managers/update.py b/dzgui/managers/update.py index ff64fb4..4bf201b 100644 --- a/dzgui/managers/update.py +++ b/dzgui/managers/update.py @@ -60,9 +60,9 @@ class UpdateManager: def _on_update_success(self) -> None: msg = dialogs.update_success dialog = QuitDialog(self.controller, msg) - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def _on_update_failure(self, msg: str) -> None: dialog = ExceptionDialog(self.controller, msg) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] diff --git a/dzgui/model/servers.py b/dzgui/model/servers.py index a1d3082..22fca92 100644 --- a/dzgui/model/servers.py +++ b/dzgui/model/servers.py @@ -432,7 +432,7 @@ class ServerModelManager: if show_dialog: dialog = ExceptionDialog(self.controller, dialogs.load_error_lan) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def _cleanup_on_failure(self, show_dialog: bool = True) -> None: # TODO: disable map, keyword, and filter widgets if model is None @@ -451,14 +451,14 @@ class ServerModelManager: dialog = ExceptionDialog(self.controller, server_timeout) dialog.set_details_buffer(kb.DZG_006) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def _push_data(self, data: list[Any]) -> None: self._get_proxy_man().push(data) self._sort_unique_maps(data) self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_success)) - def _sort_unique_maps(self, data: list) -> None: + def _sort_unique_maps(self, data: list[Any]) -> None: u_maps = set([row[1] for row in data]) self._set_new_maps(sorted(u_maps)) diff --git a/dzgui/views/components/filter_panel.py b/dzgui/views/components/filter_panel.py index 75c51e9..59c0e46 100644 --- a/dzgui/views/components/filter_panel.py +++ b/dzgui/views/components/filter_panel.py @@ -42,7 +42,10 @@ class ButtonGrid(Gtk.Grid): self.checks: list[Gtk.CheckButton] = [] flowbox = Gtk.FlowBox( - halign=Gtk.Align.CENTER, min_children_per_line=3, max_children_per_line=3, selection_mode=Gtk.SelectionMode.NONE + halign=Gtk.Align.CENTER, + min_children_per_line=3, + max_children_per_line=3, + selection_mode=Gtk.SelectionMode.NONE, ) for check in defaults.keys(): @@ -305,6 +308,7 @@ class FilterPanel(Gtk.Box): Gdk.KEY_0: 9, Gdk.KEY_minus: 10, Gdk.KEY_backslash: 11, + Gdk.KEY_apostrophe: 12, } if keyval not in mappings: return False diff --git a/dzgui/views/dialogs/wizard.py b/dzgui/views/dialogs/wizard.py index 5dbcdb4..9a44e09 100644 --- a/dzgui/views/dialogs/wizard.py +++ b/dzgui/views/dialogs/wizard.py @@ -14,7 +14,7 @@ from dzgui.const.constants import ( HERO_PATH, FLATPAK_ROOT_PATH, LEGACY_CONFIG_PATH, - TMP_PATH + TMP_PATH, ) from dzgui.const.boilerplate import config_boilerplate from dzgui.const.endpoints import STEAM_API_SETUP @@ -168,7 +168,12 @@ class NotificationFrame(Gtk.Frame): class APIValidationPage(EnumeratedWizardPage): def __init__( - self, enum: PageNum, heading: str, description: str, link: str, func: Callable + self, + enum: PageNum, + heading: str, + description: str, + link: str, + func: Callable[[str], None], ) -> None: super().__init__( enum=enum, diff --git a/dzgui/views/pages/options.py b/dzgui/views/pages/options.py index b5633ca..718c556 100644 --- a/dzgui/views/pages/options.py +++ b/dzgui/views/pages/options.py @@ -55,7 +55,7 @@ class SubmitField(Gtk.Box): ) -> None: super().__init__(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) - self.callback: Callable + self.callback: Callable[[], None] self.controller = controller self.context = context @@ -143,7 +143,7 @@ class SubmitField(Gtk.Box): return self.save_option() - def set_callback(self, callback: Callable) -> None: + def set_callback(self, callback: Callable[[], None]) -> None: self.callback = callback def save_option(self) -> None: @@ -210,7 +210,7 @@ class NameSubmitField(SubmitField): Preferences.NAME, private=False, ) - self.entry.set_width_chars(30) # type: ignore + self.entry.set_width_chars(30) self.set_callback(self.save_player_name) def save_player_name(self) -> None: @@ -439,7 +439,7 @@ class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore if prefs.paths.config.is_file() is False: dialog = ExceptionDialog(self.controller, strings.config_not_found) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] raise OSError(f"Config file '{prefs.paths.config}' not found") config = query.get_config(prefs.paths.config) diff --git a/dzgui/views/pages/preconnect.py b/dzgui/views/pages/preconnect.py index b737ff0..983e760 100644 --- a/dzgui/views/pages/preconnect.py +++ b/dzgui/views/pages/preconnect.py @@ -231,7 +231,7 @@ class PreConnectionAssistant(Gtk.Box): args = self.controller.get_debug_args() d = DebugDialog(self.controller, args) d.show_all() - d.run() + d.run() # type: ignore[no-untyped-call] def _on_ok_clicked(self, button: Gtk.Button) -> None: self.controller.update_and_connect() diff --git a/dzgui/views/trees/tree_servers.py b/dzgui/views/trees/tree_servers.py index 3143c6d..284b9c6 100644 --- a/dzgui/views/trees/tree_servers.py +++ b/dzgui/views/trees/tree_servers.py @@ -406,7 +406,7 @@ class ServerTreeView(ContextMixin, TreeView): # type: ignore path = model.get_path(it) row_index = path.get_indices()[0] try: - start, end = self.get_visible_range() + start, end = self.get_visible_range() # type: ignore[no-untyped-call] except Exception: return if row_index >= start[0] <= end[0]: From da18b4113604429b8b62b4d8be02eabd86d493d0 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 05:09:02 +0900 Subject: [PATCH 17/34] chore: drop Soup2 stubs --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7d97ce6..65a3d7f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -104,7 +104,7 @@ python-a2s = { path = "./dzgui/lib/a2s", editable = true } dayzquery = { path = "./dzgui/lib/dayzquery", editable = true } [tool.uv] -config-settings-package = { pygobject-stubs = { config = "Gtk3,Gdk3,Soup2" } } +config-settings-package = { pygobject-stubs = { config = "Gtk3,Gdk3" } } [tool.pytest.ini_options] markers = [ From 9f36569fd41c1c41dd68da9a9677ee5eb9295afe Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 05:10:56 +0900 Subject: [PATCH 18/34] drop: LoggerAlertsButton --- dzgui/views/components/buttons.py | 22 ---------------------- dzgui/views/components/statusbar.py | 4 ---- 2 files changed, 26 deletions(-) diff --git a/dzgui/views/components/buttons.py b/dzgui/views/components/buttons.py index bb3d45d..13a8e91 100644 --- a/dzgui/views/components/buttons.py +++ b/dzgui/views/components/buttons.py @@ -252,28 +252,6 @@ class AddButton(IconTextButton): self.set_tooltip_text(connect_panel.add_tooltip) -class LoggerAlertsButton(IconTextButton): - def __init__(self, warnings: int, errors: int) -> None: - warnings_text = "" - errors_text = "" - separator = "" - if warnings > 0: - warnings_plural = pluralize("warnings", warnings) - warnings_text = f"{warnings} {warnings_plural}" - if errors > 0: - errors_plural = pluralize("errors", errors) - errors_text = f" {errors} {errors_plural}" - if warnings + errors > 1: - separator = "," - concat = warnings_text + separator + errors_text - super().__init__( - icon=WARNING, - label=f"Loaded with{concat}", - ) - self.set_halign(Gtk.Align.END) - self.set_hexpand(True) - self.set_tooltip_text(alert_button_tooltip) - class SpinnerButton(Gtk.Button): def __init__(self, label: str) -> None: diff --git a/dzgui/views/components/statusbar.py b/dzgui/views/components/statusbar.py index 7eb75a3..b9dc49b 100644 --- a/dzgui/views/components/statusbar.py +++ b/dzgui/views/components/statusbar.py @@ -2,7 +2,6 @@ from typing import Self, Union, TYPE_CHECKING from dzgui.const.enum import NotebookPage, ServerTab from dzgui.util.strings import esc_to_return, question_to_return -from dzgui.views.components.buttons import LoggerAlertsButton import gi @@ -47,9 +46,6 @@ class Statusbar(Gtk.Grid): self.emitter.connect("servers_loaded", self._on_servers_loaded) self.emitter.connect("mods_updated", self._on_mods_updated) - def _on_alerts_clicked(self, button: LoggerAlertsButton) -> None: - self.controller.populate_log() - def _on_mods_updated(self, emitter: "Emitter", msg: str, mods: int) -> None: self.set_by_context(NotebookPage.MODS, msg) From bfe9b9d147f41eed6e935dfc5b3ae708bc1b6796 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 05:58:02 +0900 Subject: [PATCH 19/34] chore: clear typehinting errors --- dzgui/controllers/mc.py | 6 +++--- dzgui/managers/contextmenu.py | 2 +- dzgui/model/model_factory.py | 2 +- dzgui/util/dist.py | 4 ++-- dzgui/util/ip.py | 2 +- dzgui/views/components/buttons.py | 2 +- dzgui/views/dialogs/calc.py | 2 +- dzgui/views/trees/tree_servers.py | 3 ++- 8 files changed, 12 insertions(+), 11 deletions(-) diff --git a/dzgui/controllers/mc.py b/dzgui/controllers/mc.py index 1a05955..db656ec 100644 --- a/dzgui/controllers/mc.py +++ b/dzgui/controllers/mc.py @@ -274,7 +274,7 @@ class Controller(GObject.GObject): except Exception as e: dialog = ExceptionDialog(self, str(e)) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def select_colorized(self) -> None: mod_man = self.mediator.modtreeview.get_mod_man() @@ -308,7 +308,7 @@ class Controller(GObject.GObject): except Exception as e: dialog = ExceptionDialog(self, str(e)) dialog.show_all() - dialog.run() + dialog.run() # type: ignore[no-untyped-call] def update_steam_api_key(self, text: str) -> None: self.config_man.update_steam_api_key(text) @@ -365,7 +365,7 @@ class Controller(GObject.GObject): filter_man = self.get_filter_man() return filter_man.get_map_store() - def get_enabled_filters(self) -> dict: + def get_enabled_filters(self) -> dict[str, bool]: filter_man = self.get_filter_man() return filter_man.get_filters() diff --git a/dzgui/managers/contextmenu.py b/dzgui/managers/contextmenu.py index 016ce7b..03e6350 100644 --- a/dzgui/managers/contextmenu.py +++ b/dzgui/managers/contextmenu.py @@ -44,7 +44,7 @@ class ContextMenuManager: # UNTHREADED case ContextMenu.ADD_NOTE: dialog = NoteDialog(self.controller) - dialog.run() + dialog.run() # type: ignore[no-untyped-call] case ContextMenu.COPY_SERVER_IP: self.copy_server_ip() case ContextMenu.COPY_SERVER_NAME: diff --git a/dzgui/model/model_factory.py b/dzgui/model/model_factory.py index 7dfffbf..a48c0d3 100644 --- a/dzgui/model/model_factory.py +++ b/dzgui/model/model_factory.py @@ -11,7 +11,7 @@ from gi.repository.Gtk import ListStore, TreeIter # noqa E402 from gi.repository import GObject # noqa E402 GTYPE_TO_PYTHON = { - GObject.type_from_name(GObject.type_name(ptype)): ptype + GObject.type_from_name(GObject.type_name(ptype)): ptype # type: ignore[no-untyped-call] for ptype in (int, float, str, bool, object) } diff --git a/dzgui/util/dist.py b/dzgui/util/dist.py index 46c2d44..20db696 100644 --- a/dzgui/util/dist.py +++ b/dzgui/util/dist.py @@ -59,7 +59,7 @@ class CalcDist: self, addr: str, enum: "ServerTab", - result_queue: "Queue", + result_queue: "Queue[tuple[str, Haversine | None, ServerTab]]", controller: "Controller", cache: dict[str, Haversine], ) -> None: @@ -72,7 +72,7 @@ class CalcDist: self.ip = self.addr dist = self.compare(self.ip) - self.result_queue.put([self.addr, dist, self.enum]) + self.result_queue.put((self.addr, dist, self.enum)) def compare(self, remote_ip: str) -> Haversine | None: prefs = self.controller.get_prefs() diff --git a/dzgui/util/ip.py b/dzgui/util/ip.py index d9a2c12..168e036 100644 --- a/dzgui/util/ip.py +++ b/dzgui/util/ip.py @@ -31,7 +31,7 @@ class ReservedIPError(Exception): pass -def ip2list(ip: str) -> list: +def ip2list(ip: str) -> list[int]: return [int(digit) for digit in ip.split(".")] diff --git a/dzgui/views/components/buttons.py b/dzgui/views/components/buttons.py index 13a8e91..ac480be 100644 --- a/dzgui/views/components/buttons.py +++ b/dzgui/views/components/buttons.py @@ -64,7 +64,7 @@ class IconButton(Gtk.Button): self.icon = Icon(icon, margin_start=margin_start, margin_end=margin_end) self.set_image(self.icon) self.set_image_position(position) - self.set_focus_on_click(False) + self.set_focus_on_click(False) # type: ignore[no-untyped-call] self.set_always_show_image(True) def swap_icon(self, icon: str) -> None: diff --git a/dzgui/views/dialogs/calc.py b/dzgui/views/dialogs/calc.py index 979cb0a..9b6267a 100644 --- a/dzgui/views/dialogs/calc.py +++ b/dzgui/views/dialogs/calc.py @@ -10,7 +10,7 @@ from dzgui.views.components.labels import BoldLabel from dzgui.views.components.box import VBox, HBox gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, GLib, GObject # type: ignore # noqa E402 +from gi.repository import Gtk, GLib, GObject # noqa E402 DAY_START_TIME = time(hour=6, minute=0, second=0) DAY_END_TIME = time(hour=18, minute=0, second=0) diff --git a/dzgui/views/trees/tree_servers.py b/dzgui/views/trees/tree_servers.py index 284b9c6..6cfac6e 100644 --- a/dzgui/views/trees/tree_servers.py +++ b/dzgui/views/trees/tree_servers.py @@ -32,6 +32,7 @@ if TYPE_CHECKING: from dzgui.controllers.mc import Controller from dzgui.controllers.emitter import Emitter from dzgui.model.model_factory import FastInsertListStore + from dzgui.util.dist import Haversine QUEUE_CHECK_DELAY = 200 @@ -64,7 +65,7 @@ class ServerTreeView(ContextMixin, TreeView): # type: ignore self.queue_id: int self.handler_id: int - self.queue: Queue = Queue() + self.queue: Queue[tuple[str, "Haversine" | None, ServerTab]] = Queue() self.seen_cache: list[Record] = [] From 8fb3462f35bb84bfd8136b4e26991d8acb1404e8 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 05:58:22 +0900 Subject: [PATCH 20/34] chore: add placeholder for row struct --- dzgui/model/proxy_model.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/dzgui/model/proxy_model.py b/dzgui/model/proxy_model.py index c892110..c5ebc66 100644 --- a/dzgui/model/proxy_model.py +++ b/dzgui/model/proxy_model.py @@ -1,5 +1,6 @@ import re +from dataclasses import dataclass from typing import Any, TYPE_CHECKING, Union from dzgui.const.enum import FilterMode @@ -16,6 +17,22 @@ DAY_REG = r"([0][6-9]|[1][0-7])" NIGHT_REG = r"([0][0-5]|[1][8-9]|[2][0-3])" +@dataclass(slots=True, frozen=True) +class ServerRow: + name: str + mapname: str + view: str + time: str + players: int + max_players: int + queue: int + ip: str + qport: int + ping: str + provider: str + modded: bool + + class ProxyModelManager: """ Manages access to cached FastInsertListStore resources and From e03c6905c6715fa9d36e4d0007a4a047269e5256 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 06:30:12 +0900 Subject: [PATCH 21/34] chore: clear typehinting errors --- dzgui/api/acf.py | 2 +- dzgui/api/mods.py | 6 +----- dzgui/config/xdg.py | 4 ++-- dzgui/util/flatpak.py | 8 +++++-- dzgui/views/dialogs/early_alert.py | 2 +- dzgui/views/dialogs/filepicker.py | 12 +++++------ dzgui/views/dialogs/uninstall.py | 8 +++---- dzgui/views/dialogs/wizard.py | 8 +++---- dzgui/views/pages/keys.py | 7 +++--- dzgui/views/pages/options.py | 5 +++-- dzgui/views/trees/tree_base.py | 34 ++++++++---------------------- 11 files changed, 41 insertions(+), 55 deletions(-) diff --git a/dzgui/api/acf.py b/dzgui/api/acf.py index ce243a4..ba36ce0 100644 --- a/dzgui/api/acf.py +++ b/dzgui/api/acf.py @@ -111,7 +111,7 @@ class WorkshopACF(ACF): def __init__(self, file: Path) -> None: super().__init__(file) - def unpack(self, d: dict, lines: list[Any] = []) -> str: + def unpack(self, d: dict[str, str], lines: list[Any] = []) -> str: t1 = "AppWorkshop" t2 = ("WorkshopItemsInstalled", "WorkshopItemDetails") for k, v in d.items(): diff --git a/dzgui/api/mods.py b/dzgui/api/mods.py index c6a076d..f8a9814 100644 --- a/dzgui/api/mods.py +++ b/dzgui/api/mods.py @@ -114,10 +114,6 @@ def get_delimited_mods(steam_path: Path) -> list[Any]: return parse_mods(mods) -def get_missing_mods(local: list, remote: list) -> list: - return [mod for mod in remote if mod not in local] - - def _hash(uid: str, use_custom: bool = False) -> str: md5 = hashlib.md5() md5.update(uid.encode("ascii")) @@ -152,7 +148,7 @@ def remove_stale_signatures( def find_stale_mods(config: Path) -> list[int]: - def push_record(rec: str) -> list: + def push_record(rec: str) -> list[int]: record = fqip_to_record(rec) if record is None: return [] diff --git a/dzgui/config/xdg.py b/dzgui/config/xdg.py index b72ea7b..532c73f 100644 --- a/dzgui/config/xdg.py +++ b/dzgui/config/xdg.py @@ -51,7 +51,7 @@ def is_writeable(path_str: str) -> bool: return True -def get_xdg_paths() -> dict: +def get_xdg_paths() -> dict[str, Path]: home = Path.home() xdg_paths = { "XDG_CONFIG_HOME": home / ".config", @@ -73,7 +73,7 @@ def get_xdg_paths() -> dict: return resolved_paths -def parse_filepaths(xdg: dict) -> Xdg: +def parse_filepaths(xdg: dict[str, Path]) -> Xdg: config = xdg["XDG_CONFIG_HOME"] state = xdg["XDG_STATE_HOME"] share = xdg["XDG_DATA_HOME"] diff --git a/dzgui/util/flatpak.py b/dzgui/util/flatpak.py index 7372fd6..d86663d 100644 --- a/dzgui/util/flatpak.py +++ b/dzgui/util/flatpak.py @@ -1,12 +1,16 @@ import logging import subprocess +from typing import TYPE_CHECKING from dzgui.const.constants import APP_NAME, FLATPAK_APPID, GTK_APP_ID, TMP_PATH logger = logging.getLogger(APP_NAME) +if TYPE_CHECKING: + from pathlib import Path -def apply_overrides(xdg: dict[str, str]) -> None: + +def apply_overrides(xdg: dict[str, "Path"]) -> None: try: # NOTE: D-Bus registration required for sandbox args = [ @@ -19,7 +23,7 @@ def apply_overrides(xdg: dict[str, str]) -> None: subprocess.run([*args], capture_output=True) paths: list[str] = [] - paths.extend([*xdg.values()]) + paths.extend([str(path) for path in xdg.values()]) paths.append(TMP_PATH) for path in paths: diff --git a/dzgui/views/dialogs/early_alert.py b/dzgui/views/dialogs/early_alert.py index b7032a0..ac4dfd4 100644 --- a/dzgui/views/dialogs/early_alert.py +++ b/dzgui/views/dialogs/early_alert.py @@ -46,7 +46,7 @@ class AbortDialog(ColorAwareApp, Gtk.MessageDialog): # type: ignore self.connect("response", self._on_response) - self.run() + self.run() # type: ignore[no-untyped-call] self.destroy() def _on_response(self, dialog: Self, response: Gtk.ResponseType) -> None: diff --git a/dzgui/views/dialogs/filepicker.py b/dzgui/views/dialogs/filepicker.py index 2048598..f72c70d 100644 --- a/dzgui/views/dialogs/filepicker.py +++ b/dzgui/views/dialogs/filepicker.py @@ -16,14 +16,14 @@ class FilePicker(Gtk.FileChooserDialog): transient_for=parent, resizable=True, ) - self.add_buttons("_Cancel", Gtk.ResponseType.CANCEL) - self.add_buttons("_Save", Gtk.ResponseType.OK) + self.add_buttons("_Cancel", Gtk.ResponseType.CANCEL) # type: ignore[no-untyped-call] + self.add_buttons("_Save", Gtk.ResponseType.OK) # type: ignore[no-untyped-call] self.set_default_response(Gtk.ResponseType.OK) self.set_current_name(picker.placeholder) def pick_file(self) -> Path | None: - res = self.run() + res = self.run() # type: ignore[no-untyped-call] if res in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT): self.destroy() return None @@ -43,12 +43,12 @@ class FolderPicker(Gtk.FileChooserDialog): parent=parent, resizable=True, ) - self.add_buttons("_Cancel", Gtk.ResponseType.CANCEL) - self.add_buttons("_OK", Gtk.ResponseType.OK) + self.add_buttons("_Cancel", Gtk.ResponseType.CANCEL) # type: ignore[no-untyped-call] + self.add_buttons("_OK", Gtk.ResponseType.OK) # type: ignore[no-untyped-call] self.set_default_response(Gtk.ResponseType.OK) def pick_folder(self) -> Path | None: - res = self.run() + res = self.run() # type: ignore[no-untyped-call] if res in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT): self.destroy() return None diff --git a/dzgui/views/dialogs/uninstall.py b/dzgui/views/dialogs/uninstall.py index 3e7d2e7..b27f1d6 100644 --- a/dzgui/views/dialogs/uninstall.py +++ b/dzgui/views/dialogs/uninstall.py @@ -22,7 +22,7 @@ from gi.repository import Gtk, GLib, Pango # noqa E402 class Assistant(Gtk.Assistant): - def __init__(self, is_deck: bool, paths: dict[str, str]): + def __init__(self, is_deck: bool, paths: dict[str, Path]): super().__init__() if is_deck: # NOTE: deemed to be "safe" dimensions that exclude taskbar size @@ -63,7 +63,7 @@ class Assistant(Gtk.Assistant): class Window(Gtk.Window): - def __init__(self, is_deck: bool, paths: dict[str, str]) -> None: + def __init__(self, is_deck: bool, paths: dict[str, Path]) -> None: super().__init__(title=APP_NAME, icon_name=APP_NAME) self.assistant = Assistant(is_deck, paths) @@ -121,7 +121,7 @@ class CheckboxWithPath(CheckboxWithLabel): class UninstallPage(ScrolledWizardPage): - def __init__(self, paths: dict[str, str], pyapp: str | None): + def __init__(self, paths: dict[str, Path], pyapp: str | None): super().__init__(heading=uninstall.pg1_heading, description=uninstall.pg1_blurb) self.pyapp = pyapp @@ -219,7 +219,7 @@ class UninstallPage(ScrolledWizardPage): class UninstallWizard(ColorAwareApp, Gtk.Application): # type: ignore - def __init__(self, is_deck: bool, paths: dict[str, str]) -> None: + def __init__(self, is_deck: bool, paths: dict[str, Path]) -> None: super().__init__() config = Path(paths["XDG_CONFIG_HOME"]) if not config.is_dir() or not config.joinpath("config.json").is_file(): diff --git a/dzgui/views/dialogs/wizard.py b/dzgui/views/dialogs/wizard.py index 9a44e09..c2f38af 100644 --- a/dzgui/views/dialogs/wizard.py +++ b/dzgui/views/dialogs/wizard.py @@ -426,7 +426,7 @@ class PreferencesPage(EnumeratedWizardPage): class FlatpakOverridePage(EnumeratedWizardPage): - def __init__(self, xdg_paths: dict[str, str]) -> None: + def __init__(self, xdg_paths: dict[str, Path]) -> None: super().__init__( enum=PageNum.FLATPAK, heading=wizard.heading_flatpak, @@ -476,7 +476,7 @@ class CompletionPage(EnumeratedWizardPage): class Assistant(Gtk.Assistant): - def __init__(self, is_deck: bool, XDG: "Xdg", xdg_paths: dict[str, str]): + def __init__(self, is_deck: bool, XDG: "Xdg", xdg_paths: dict[str, Path]): super().__init__() if is_deck: # NOTE: deemed to be "safe" dimensions that exclude taskbar size @@ -781,7 +781,7 @@ class SteamPathPage(EnumeratedWizardPage): class SetupWizard(ColorAwareApp, Gtk.Application): # type: ignore - def __init__(self, is_deck: bool, XDG: "Xdg", xdg_paths: dict[str, str]) -> None: + def __init__(self, is_deck: bool, XDG: "Xdg", xdg_paths: dict[str, Path]) -> None: super().__init__() GLib.set_prgname(APP_NAME) self.win = Window(is_deck, XDG, xdg_paths) @@ -792,7 +792,7 @@ class SetupWizard(ColorAwareApp, Gtk.Application): # type: ignore class Window(Gtk.Window): - def __init__(self, is_deck: bool, XDG: "Xdg", xdg_paths: dict[str, str]) -> None: + def __init__(self, is_deck: bool, XDG: "Xdg", xdg_paths: dict[str, Path]) -> None: super().__init__(title=APP_NAME, icon_name=APP_NAME) self.assistant = Assistant(is_deck, XDG, xdg_paths) diff --git a/dzgui/views/pages/keys.py b/dzgui/views/pages/keys.py index ea12476..1da56d3 100644 --- a/dzgui/views/pages/keys.py +++ b/dzgui/views/pages/keys.py @@ -13,6 +13,7 @@ if TYPE_CHECKING: from dzgui.controllers.mc import Controller +# TODO: refactor into autopopulated ListStores class Keybindings(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore """ Notebook page holding a prearranged grid @@ -35,7 +36,7 @@ class Keybindings(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore self.connect("key-press-event", self._on_keypress) - def build_keys(self, items: list) -> Gtk.Grid: + def build_keys(self, items: list[dict[str, str]]) -> Gtk.Grid: grid = Gtk.Grid(row_spacing=10, column_homogeneous=True) grid.set_halign(Gtk.Align.START) row, col = 1, 0 @@ -72,7 +73,7 @@ class Keybindings(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore row += 1 return grid - def build_sidebar(self, categories: list) -> Gtk.Grid: + def build_sidebar(self, categories: list[str]) -> Gtk.Grid: row, col = 0, 0 w, h = 1, 1 sidebar = Gtk.Grid(row_homogeneous=True, orientation=Gtk.Orientation.VERTICAL) @@ -83,7 +84,7 @@ class Keybindings(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore sidebar.attach(label, col, row, w, h) return sidebar - def build_grid(self, items: list) -> Gtk.Grid: + def build_grid(self, items: list[dict[str, str]]) -> Gtk.Grid: grid = Gtk.Grid( row_spacing=20, halign=Gtk.Align.CENTER, diff --git a/dzgui/views/pages/options.py b/dzgui/views/pages/options.py index 718c556..6269e89 100644 --- a/dzgui/views/pages/options.py +++ b/dzgui/views/pages/options.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Callable, TYPE_CHECKING +from typing import Any, Callable, TYPE_CHECKING from dzgui.api import pefile as PeFile from dzgui.api.steam import get_last_updated @@ -406,7 +406,8 @@ class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore def _on_link_button_clicked(self, button: Gtk.Button, url: str) -> None: open_links.open_link_by_url(url) - def _make_grid(self, rows: list) -> Gtk.Grid: + # TODO: use FlowBox + def _make_grid(self, rows: list[list[Any]]) -> Gtk.Grid: grid = Gtk.Grid( orientation=Gtk.Orientation.VERTICAL, column_spacing=10, diff --git a/dzgui/views/trees/tree_base.py b/dzgui/views/trees/tree_base.py index 5d7b6a2..0919b08 100644 --- a/dzgui/views/trees/tree_base.py +++ b/dzgui/views/trees/tree_base.py @@ -79,11 +79,6 @@ class TreeView(CursorMixin, Gtk.TreeView): # type: ignore index = path.get_indices()[0] return index - def get_selected_records(self) -> list: - sel = self.get_selection() - model, rows = sel.get_selected_rows() - return [model[row] for row in rows] - def _on_keypress(self, treeview: Gtk.TreeView, event: Gdk.EventKey) -> bool: from dzgui.views.trees.tree_servers import ServerTreeView @@ -145,9 +140,11 @@ class TreeView(CursorMixin, Gtk.TreeView): # type: ignore self.set_cursor(path) def get_value_at_index(self, index: int) -> Any: - (model, pathlist) = self.get_model_and_pathlist() + model, pathlist = self.get_model_and_pathlist() if len(pathlist) < 1: return "" + if model is None: + return "" path = pathlist[0] tree_iter = model.get_iter(path) value = model.get_value(tree_iter, index) @@ -161,20 +158,16 @@ class TreeView(CursorMixin, Gtk.TreeView): # type: ignore sel = self.get_selection() self._on_tree_selection_changed(sel) - def get_model_and_pathlist(self) -> tuple: + def get_model_and_pathlist( + self, + ) -> tuple[Gtk.TreeModel | None, list[Gtk.TreePath]]: select = self.get_selection() sels = select.get_selected_rows() - (model, pathlist) = sels + model: Gtk.TreeModel | None + pathlist: list[Gtk.TreePath] + model, pathlist = sels return (model, pathlist) - # @deprecated("Currently unused") - # def get_mpath(self) -> Optional[Gtk.TreePath]: - # (model, pathlist) = self.get_model_and_pathlist() - # if len(pathlist) < 1: - # return None - # path = pathlist[0] - # return path - def _on_row_activated( self, treeview: Gtk.TreeView, @@ -189,15 +182,6 @@ class TreeView(CursorMixin, Gtk.TreeView): # type: ignore return True return False - # @deprecated("unused") - # def get_selected_row(self) -> Optional[Gtk.TreeModelRow]: - # ind = self.get_selected_row_index() - # model = self.get_model() - # if model is None: - # return None - # row = model[ind] - # return row - def get_col_value_by_path_index(self, path: Gtk.TreePath, index: int) -> Any: model = self.get_model() if model is None: From 0c4026daf8bc0d3c542b484b2f63007450396fc7 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 10:02:41 +0900 Subject: [PATCH 22/34] chore: clear typehinting errors --- dzgui/api/acf.py | 3 ++- dzgui/api/servers.py | 10 ++++++---- dzgui/controllers/mc.py | 6 +++--- dzgui/managers/filter.py | 5 ++--- dzgui/model/proxy_model.py | 10 +++------- dzgui/util/format.py | 3 ++- dzgui/views/components/buttons.py | 14 ++++---------- dzgui/views/components/connect_panel.py | 2 +- dzgui/views/components/entry.py | 9 ++++----- dzgui/views/components/filter_panel.py | 4 ++-- dzgui/views/dialogs/boot.py | 2 +- dzgui/views/dialogs/generic.py | 2 +- dzgui/views/mixins/context_mixin.py | 6 ++---- 13 files changed, 33 insertions(+), 43 deletions(-) diff --git a/dzgui/api/acf.py b/dzgui/api/acf.py index ba36ce0..4b1dc71 100644 --- a/dzgui/api/acf.py +++ b/dzgui/api/acf.py @@ -111,7 +111,8 @@ class WorkshopACF(ACF): def __init__(self, file: Path) -> None: super().__init__(file) - def unpack(self, d: dict[str, str], lines: list[Any] = []) -> str: + def unpack(self, d: dict[str, Any], lines: list[Any] = []) -> str: + self.indent: int t1 = "AppWorkshop" t2 = ("WorkshopItemsInstalled", "WorkshopItemDetails") for k, v in d.items(): diff --git a/dzgui/api/servers.py b/dzgui/api/servers.py index 8c331e4..6ac446b 100644 --- a/dzgui/api/servers.py +++ b/dzgui/api/servers.py @@ -111,7 +111,7 @@ def get_netmask() -> str: # TODO: rename -def test_ip(suffix: int, port: int, event: threading.Event) -> dict | None: +def test_ip(suffix: int, port: int, event: threading.Event) -> dict[str, Any] | None: if event.is_set(): return None netmask = get_netmask() @@ -139,7 +139,9 @@ def sanitize(name: str) -> str: return name -def parse_json(json: list) -> list: +def parse_json( + json: list[dict[str, Any]], +) -> list[tuple[str, str, str, str, int, int, int, str, int, int, str, bool]]: """ Server metadata is underspecified and server operators tend to insert random garbage in the headers. In case @@ -507,14 +509,14 @@ def record_to_fqip(record: Record) -> str: return f"{record.ip}:{record.gameport}:{record.qport}" -def response_to_fqip(res: dict) -> str: +def response_to_fqip(res: dict[str, Any]) -> str: ip = res["addr"].split(":")[0] gameport = res["gameport"] qport = res["addr"].split(":")[1] return f"{ip}:{gameport}:{qport}" -def response_to_record(res: dict) -> Record: +def response_to_record(res: dict[str, Any]) -> Record: ip = res["addr"].split(":")[0] gameport = res["gameport"] qport = res["addr"].split(":")[1] diff --git a/dzgui/controllers/mc.py b/dzgui/controllers/mc.py index db656ec..6074046 100644 --- a/dzgui/controllers/mc.py +++ b/dzgui/controllers/mc.py @@ -77,7 +77,7 @@ class AppNavigation: class Controller(GObject.GObject): def __init__(self) -> None: - self.dist_cache: dict[str, "Haversine"] = {} + self.dist_cache: dict[str, "Haversine" | None] = {} self.mediator = AppNavigation() self.prefs: UserPrefs @@ -355,10 +355,10 @@ class Controller(GObject.GObject): filter_man.set_filter(filt, state) self.mediator.filters.set_filters(filters) - def get_dist_cache(self) -> dict[str, "Haversine"]: + def get_dist_cache(self) -> dict[str, Union["Haversine", None]]: return self.dist_cache - def set_dist_cache(self, addr: str, haversine: "Haversine") -> None: + def set_dist_cache(self, addr: str, haversine: Union["Haversine", None]) -> None: self.dist_cache[addr] = haversine def get_map_store(self) -> Gtk.ListStore: diff --git a/dzgui/managers/filter.py b/dzgui/managers/filter.py index f3f175c..357318f 100644 --- a/dzgui/managers/filter.py +++ b/dzgui/managers/filter.py @@ -51,7 +51,6 @@ class FilterManager: self.active_map = (0, all_maps) self.prior_map = all_maps - self.filters: list self.enabled_filters = dict(self.default_filters) def get_tooltips(self) -> dict[str, str]: @@ -75,7 +74,7 @@ class FilterManager: def set_active_keyword(self, word: str) -> None: self.active_keyword = word - def get_default_filters(self) -> dict: + def get_default_filters(self) -> dict[str, bool]: """Deep copy of defaults""" return dict(self.default_filters) @@ -116,7 +115,7 @@ class FilterManager: def get_unique_maps(self) -> list[str]: return [row[0] for row in self.map_store if row[0] != "All maps"] - def get_all_filters(self) -> tuple: + def get_all_filters(self) -> tuple[str, ...]: map_name = self.get_active_map_name() enabled = self.get_filters() kw = self.get_active_keyword() diff --git a/dzgui/model/proxy_model.py b/dzgui/model/proxy_model.py index c5ebc66..023da66 100644 --- a/dzgui/model/proxy_model.py +++ b/dzgui/model/proxy_model.py @@ -47,7 +47,7 @@ class ProxyModelManager: def __init__(self, filter_man: "FilterManager") -> None: self.filter_cache: dict[ - tuple[str], + tuple[str, ...], list[tuple[str, str, str, str, int, int, int, str, int, int, str, bool]], ] = {} @@ -68,9 +68,6 @@ class ProxyModelManager: return False return True - def append_row(self, row: list) -> None: - self.proxy_model.append(row) - def append_row_to_control( self, row: tuple[str, str, str, str, int, int, int, str, int, int, str, bool] ) -> None: @@ -84,7 +81,6 @@ class ProxyModelManager: self.proxy_model[treeiter][4] = playercount.players self.proxy_model[treeiter][6] = playercount.queue - # TODO: use dataclass for record rows def append_row_to_history( self, history: tuple[str, str, str, str, int, int, int, str, int, int, str, bool], @@ -264,7 +260,7 @@ class ProxyModelManager: rows = [row for row in rows if row[11]] return rows - def filter_toggle_on(self, filters: tuple[str]) -> list: + def filter_toggle_on(self, filters: tuple[str, ...]) -> list: """Effectively applies all filters""" self.set_filtered(self.control_model) self.set_filtered(self.filter_map(filters)) @@ -275,7 +271,7 @@ class ProxyModelManager: self.set_filtered(self.filter_toggle_off(filters, f)) return self.filtered - def set_cache(self, filters: tuple[str], filtered_rows: list[tuple]) -> None: + def set_cache(self, filters: tuple[str, ...], filtered_rows: list[tuple]) -> None: self.filter_cache[filters] = filtered_rows def set_filtered(self, rows: list | None) -> None: diff --git a/dzgui/util/format.py b/dzgui/util/format.py index e7b4d07..203ab3f 100644 --- a/dzgui/util/format.py +++ b/dzgui/util/format.py @@ -1,4 +1,5 @@ import re +from typing import Any from dzgui.util.localize import number from dzgui.strings.generic import selectable_tree @@ -60,7 +61,7 @@ def format_server_mods(mods: int) -> str: return f"Found {mods:n} {plural}. {workshop}" -def format_player_count(model: Gtk.TreeModel | None, control: list) -> str: +def format_player_count(model: Gtk.TreeModel | None, control: list[Any]) -> str: players = 0 hits: int status: str diff --git a/dzgui/views/components/buttons.py b/dzgui/views/components/buttons.py index ac480be..cd150c7 100644 --- a/dzgui/views/components/buttons.py +++ b/dzgui/views/components/buttons.py @@ -1,10 +1,8 @@ from typing import Callable, Literal, Self, TYPE_CHECKING, Union from dzgui.util.clip import copy_clipboard -from dzgui.util.format import pluralize from dzgui.strings import buttons from dzgui.util.strings import ( - alert_button_tooltip, atomic_buttons, ) from dzgui.const.constants import ( @@ -14,7 +12,6 @@ from dzgui.const.constants import ( LIST_ADD, REFRESH_ICON, STEAM_ICON, - WARNING, WEB_BROWSER, ) from dzgui.strings import connect_panel @@ -101,15 +98,14 @@ class LargeIconTextButton(IconButton): class ClipboardButton(IconTextButton): - def __init__(self, controller: Union["Controller", None], func: Callable) -> None: + def __init__(self, func: Callable[[], str]) -> None: super().__init__(CLIPBOARD, atomic_buttons.copy) - self.controller = controller self.connect("clicked", self._on_button_clicked, func) # TODO: strings self.set_tooltip_text("Copy to clipboard") - def _on_button_clicked(self, button: Self, func: Callable) -> None: + def _on_button_clicked(self, button: Self, func: Callable[[], str]) -> None: def revert() -> Literal[False]: self.revert_icon() return False @@ -120,10 +116,9 @@ class ClipboardButton(IconTextButton): GLib.timeout_add(600, revert) -# TODO: determine when controller would be passed to this button or drop class CopyIpButton(ClipboardButton): - def __init__(self, controller: Union["Controller", None], func: Callable) -> None: - super().__init__(controller, func) + def __init__(self, func: Callable[[], str]) -> None: + super().__init__(func) # TODO: strings self.set_tooltip_text("Copy IP to clipboard") @@ -252,7 +247,6 @@ class AddButton(IconTextButton): self.set_tooltip_text(connect_panel.add_tooltip) - class SpinnerButton(Gtk.Button): def __init__(self, label: str) -> None: super().__init__() diff --git a/dzgui/views/components/connect_panel.py b/dzgui/views/components/connect_panel.py index 17d0a71..0f91f84 100644 --- a/dzgui/views/components/connect_panel.py +++ b/dzgui/views/components/connect_panel.py @@ -187,7 +187,7 @@ class FavPanel(Gtk.Frame): self.fav_button = SteamConnectButton() self.fav_button.connect("clicked", self._on_connect_clicked) - self.copy_button = CopyIpButton(self.controller, self.get_fav_ip) + self.copy_button = CopyIpButton(self.get_fav_ip) # TODO: strings self.unset_button = CloseButton("Unset") diff --git a/dzgui/views/components/entry.py b/dzgui/views/components/entry.py index e1b3e7e..408ead8 100644 --- a/dzgui/views/components/entry.py +++ b/dzgui/views/components/entry.py @@ -38,7 +38,7 @@ class ValidatedEntry(Gtk.Entry): def __init__( self, controller: "Controller", - func: Callable, + func: Callable[[str], bool], placeholder_text: str = "", tooltip_text: str = "", ) -> None: @@ -46,7 +46,6 @@ class ValidatedEntry(Gtk.Entry): hexpand=True, placeholder_text=placeholder_text, tooltip_text=tooltip_text ) - self.func: Callable self.set_validation_func(func) self.controller = controller @@ -72,7 +71,7 @@ class ValidatedEntry(Gtk.Entry): self.emit("string_validated", False) remove_class(self, self.classname) - def set_validation_func(self, func: Callable) -> None: + def set_validation_func(self, func: Callable[[str], bool]) -> None: self.func = func def insert_icon(self) -> None: @@ -141,7 +140,7 @@ class APIEntry(Gtk.Box): def __init__(self) -> None: super().__init__(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) - self.func: Callable | None = None + self.func: Callable[[str], None] | None = None # TODO: strings self.entry = Gtk.Entry( @@ -222,7 +221,7 @@ class APIEntry(Gtk.Box): res = self.func(text) return res - def set_validation_func(self, func: Callable | None) -> None: + def set_validation_func(self, func: Callable[[str], None] | None) -> None: self.func = func def disable_button(self) -> None: diff --git a/dzgui/views/components/filter_panel.py b/dzgui/views/components/filter_panel.py index 59c0e46..2fa69a5 100644 --- a/dzgui/views/components/filter_panel.py +++ b/dzgui/views/components/filter_panel.py @@ -30,7 +30,7 @@ class ButtonGrid(Gtk.Grid): def __init__( self, controller: "Controller", - defaults: dict[str, str], + defaults: dict[str, bool], tooltips: dict[str, str], ) -> None: super().__init__( @@ -96,7 +96,7 @@ class ButtonGrid(Gtk.Grid): logger.info(f"User toggled button '{label}' to {state}") self.emitter.emit("check_toggled", label, state) - def get_checkboxes(self) -> list: + def get_checkboxes(self) -> list[Gtk.CheckButton]: return self.checks diff --git a/dzgui/views/dialogs/boot.py b/dzgui/views/dialogs/boot.py index d4aa557..f3809a6 100644 --- a/dzgui/views/dialogs/boot.py +++ b/dzgui/views/dialogs/boot.py @@ -105,7 +105,7 @@ class BootDialog(ColorAwareApp, Gtk.Dialog): # type: ignore self.error_label = Gtk.Label(margin_start=15, margin_end=15) # TODO: abstract class - self.copy_button = ClipboardButton(None, lambda: self.error_label.get_text()) + self.copy_button = ClipboardButton(self.error_label.get_text) self.button_hbox = Gtk.Box( orientation=Gtk.Orientation.HORIZONTAL, halign=Gtk.Align.CENTER, spacing=10 ) diff --git a/dzgui/views/dialogs/generic.py b/dzgui/views/dialogs/generic.py index cff1e2a..08dc77b 100644 --- a/dzgui/views/dialogs/generic.py +++ b/dzgui/views/dialogs/generic.py @@ -187,7 +187,7 @@ class TextBufferDialog(GenericDialog): secondary=secondary, ) self.text = text - copy_button = ClipboardButton(controller, self.get_text) + copy_button = ClipboardButton(self.get_text) self.add_action_widget(copy_button, Gtk.ResponseType.NONE) self.add_button("OK", Gtk.ResponseType.OK) self.connect("response", self._on_response) diff --git a/dzgui/views/mixins/context_mixin.py b/dzgui/views/mixins/context_mixin.py index 5befa2c..2fd7ca1 100644 --- a/dzgui/views/mixins/context_mixin.py +++ b/dzgui/views/mixins/context_mixin.py @@ -15,9 +15,7 @@ logger = logging.getLogger(APP_NAME) class ContextMixin(TreeView): - def present_menu( - self, widget: Gtk.Widget, event: Gdk.Event - ) -> bool: + def present_menu(self, widget: Gtk.Widget, event: Gdk.Event) -> bool: if self.menu is None: return False @@ -26,7 +24,7 @@ class ContextMixin(TreeView): match event.type: case Gdk.EventType.BUTTON_PRESS: - if event.button != 3: + if event.button != Gdk.BUTTON_SECONDARY: # type: ignore[comparison-overlap] return False self._process_button_event(event) # type: ignore case Gdk.EventType.KEY_PRESS: From 06c85fdfd8183b87c784278fa2fea6cca45d516e Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Wed, 11 Nov 2026 01:12:51 +0900 Subject: [PATCH 23/34] chore: add remarks about release structure --- HISTORY.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 019b55e..63a5323 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -18,7 +18,7 @@ Due to user interest, features continued to be added. This early tool is archive ### 2022-05-09: addition of zenity GUI and project name change (DZGUI) With the introduction of the Steam Deck, more users wanted a graphical user interface to interact with DZTUI. Being that DZTUI was purely a Bash script, a method of -drawing menus was needed, so the 'zenity' package was used to create prefabricated dialogs. +drawing menus was needed, so the "zenity" package was used to create prefabricated dialogs. While this met the minimum requirement to show prompts and selectable options, no rich customization was possible. The introduction of zenity as a dependency marked the shift from "DZTUI" to "DZGUI". @@ -101,8 +101,13 @@ The maintainers of those projects were advised to excise the offending segments ### 2026-05-17: Switch to standalone releases The DZGUI 7 beta starts shipping standalone glibc builds with an integrated setup wizard and installer. -# TODO: remarks on DZGUI 6 stable vs testing branch logic and "over the air" updates vs standalone builds -# TODO: note archived branch names +DZGUI 6 had relied on "over the air" updates delivered via either a "stable" or "testing" branch +(archived respectively under the "dzgui" and "testing" branches). Effectively, the script would bootstrap itself and +fetch raw files directly from the repository. This was prone to problems because updates were monolithic, not giving +an easy escape hatch if the user needed to rollback in the event of a regression. + +This was changed to atomic, tagged releases. In addition to updating directly through DZGUI, these prebuilt releases can be +downloaded manually as needed, and rollbacks are available for all prior versions under the DZGUI 7 specification. ### 2026-10-27: 500 tickets closed The milestone of 500 tickets/PRs being closed was reached. From 30fa762780485b0015847e9c81c19e9485ce1fca Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Wed, 11 Nov 2026 01:35:12 +0900 Subject: [PATCH 24/34] fix: update bool --- dzgui/views/components/right_panel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dzgui/views/components/right_panel.py b/dzgui/views/components/right_panel.py index a5efa3c..916e2de 100644 --- a/dzgui/views/components/right_panel.py +++ b/dzgui/views/components/right_panel.py @@ -71,7 +71,7 @@ class RightPanel(Gtk.Box): for el in self.button_vbox, self.keys, self.filters_vbox, self.refresh_button: self.pack_start(el, expand=False, fill=True, padding=0) - self.pack_start(self.sel_panel, expand=False, fill=True, padding=0) + self.pack_start(self.sel_panel, expand=False, fill=False, padding=0) self.update_button = IconTextButton( "dialog-information-symbolic", label="Updates available" From d6b5c7bbb42113ceebb307309c85c180c16a3b08 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Wed, 11 Nov 2026 01:45:00 +0900 Subject: [PATCH 25/34] chore: drop unused imports --- dzgui/views/components/filter_panel.py | 1 - dzgui/views/pages/options.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/dzgui/views/components/filter_panel.py b/dzgui/views/components/filter_panel.py index b2259f2..faa5185 100644 --- a/dzgui/views/components/filter_panel.py +++ b/dzgui/views/components/filter_panel.py @@ -3,7 +3,6 @@ from typing import Literal, TYPE_CHECKING from dzgui.const.constants import ( APP_NAME, - SEARCH_ICON, ) from dzgui.const.icons import ICON_SEARCH from dzgui.util.format import format_exception diff --git a/dzgui/views/pages/options.py b/dzgui/views/pages/options.py index e40a3d2..990dc58 100644 --- a/dzgui/views/pages/options.py +++ b/dzgui/views/pages/options.py @@ -13,8 +13,6 @@ from dzgui.const.constants import ( FLATPAK_RUN_CMD, FLATPAK_SANDBOX, FLATPAK_STEAM_PATH, - VIEW_CONCEAL, - VIEW_REVEAL, ) from dzgui.const.icons import ICON_VIEW_CONCEAL, ICON_VIEW_REVEAL from dzgui.const.endpoints import STEAM_API_SETUP From 5a1c098af767317b67e3db1fae4d68e7accf2111 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Wed, 11 Nov 2026 03:09:14 +0900 Subject: [PATCH 26/34] fix: acquire lock prior to calling build_gui() --- dzgui/app_init.py | 3 --- dzgui/main.py | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dzgui/app_init.py b/dzgui/app_init.py index 178da23..4903b20 100644 --- a/dzgui/app_init.py +++ b/dzgui/app_init.py @@ -22,7 +22,6 @@ from dzgui.init.migrate import ( copy_state_files, migrate_cols_file, ) -from dzgui.init.flock import lock_acquire from dzgui.init.prereqs import has_flatpak_client, has_steam_client from dzgui.strings import boot @@ -122,8 +121,6 @@ def load_steam_icon() -> None: def build_gui(version: str, is_debug: bool) -> App | None: - lock = lock_acquire() # noqa - set_locale() # NOTE: consider aborting this check if steam deck xdg_paths = get_xdg_paths() diff --git a/dzgui/main.py b/dzgui/main.py index 5e660cf..a32d021 100644 --- a/dzgui/main.py +++ b/dzgui/main.py @@ -43,6 +43,9 @@ def main() -> None: # NOTE: only import ligbirepository modules after the above check from dzgui.app_init import build_gui + from dzgui.init.flock import lock_acquire + + lock = lock_acquire() # noqa app = build_gui(version, args.debug) if app is None: From 18793478315b3b403c074c61020787f2fe27adab Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Wed, 11 Nov 2026 03:09:39 +0900 Subject: [PATCH 27/34] chore: add typehints --- tests/test_paused_downloads.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_paused_downloads.py b/tests/test_paused_downloads.py index 86bbb70..1c45ca2 100644 --- a/tests/test_paused_downloads.py +++ b/tests/test_paused_downloads.py @@ -16,7 +16,7 @@ from dzgui.api.steam import is_dayz_downloading (["state_221100_1111"], False), ], ) -def test_dayz_patch_pending(monkeypatch, file_list, expect) -> None: +def test_dayz_patch_pending(monkeypatch, file_list: list[str], expect: bool) -> None: def mock_get_pending_downloads(steam_path: Path, appid: int) -> None: files = [Path(file) for file in file_list] return files From bb04854cd7311ecfd2e1382f00ee8affc68fcd8e Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Wed, 11 Nov 2026 03:17:29 +0900 Subject: [PATCH 28/34] chore: add keybinding to strings --- dzgui/util/strings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dzgui/util/strings.py b/dzgui/util/strings.py index 931ca88..21790a9 100644 --- a/dzgui/util/strings.py +++ b/dzgui/util/strings.py @@ -118,6 +118,7 @@ servers = { "0": "toggle filter 10", "Minus": "toggle filter 11", "Backslash": "toggle filter 12", + "Apostrophe": "toggle filter 13", } vim = { "j": "Move down a row/scroll up", From 8aa7328d8f3a30fb5312d8926526948acd94ac36 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Wed, 11 Nov 2026 03:18:40 +0900 Subject: [PATCH 29/34] chore: drop unused import --- dzgui/views/components/buttons.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dzgui/views/components/buttons.py b/dzgui/views/components/buttons.py index cd150c7..a913168 100644 --- a/dzgui/views/components/buttons.py +++ b/dzgui/views/components/buttons.py @@ -1,4 +1,4 @@ -from typing import Callable, Literal, Self, TYPE_CHECKING, Union +from typing import Callable, Literal, Self, TYPE_CHECKING from dzgui.util.clip import copy_clipboard from dzgui.strings import buttons From cc0354c8810029d8c88865f4e80d450de6f15159 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Wed, 11 Nov 2026 03:21:31 +0900 Subject: [PATCH 30/34] chore: drop unused imports --- dzgui/views/components/buttons.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dzgui/views/components/buttons.py b/dzgui/views/components/buttons.py index 2fb62eb..a8c9faf 100644 --- a/dzgui/views/components/buttons.py +++ b/dzgui/views/components/buttons.py @@ -1,4 +1,4 @@ -from typing import Callable, Literal, Self, TYPE_CHECKING +from typing import Callable, Literal, Self, TYPE_CHECKING, Union from dzgui.util.clip import copy_clipboard from dzgui.strings import buttons @@ -12,7 +12,6 @@ from dzgui.const.icons import ( ICON_LIST_ADD, ICON_REFRESH, ICON_STEAM, - ICON_WARNING, ICON_WEB_BROWSER, ) from dzgui.strings import connect_panel From 134dcb6f0bb29a92adeebfa8927c43473acf9fb2 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Wed, 11 Nov 2026 03:27:12 +0900 Subject: [PATCH 31/34] fix: unused param blocking boot --- dzgui/views/components/buttons.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dzgui/views/components/buttons.py b/dzgui/views/components/buttons.py index a8c9faf..53e89d8 100644 --- a/dzgui/views/components/buttons.py +++ b/dzgui/views/components/buttons.py @@ -98,9 +98,8 @@ class LargeIconTextButton(IconButton): class ClipboardButton(IconTextButton): - def __init__(self, controller: Union["Controller", None], func: Callable) -> None: + def __init__(self, func: Callable) -> None: super().__init__(ICON_CLIPBOARD, atomic_buttons.copy) - self.controller = controller self.connect("clicked", self._on_button_clicked, func) self.set_tooltip_text(buttons.clipboard_button) From 88f6b84bea4ead5cf73f62d3dc3c882a39a96c7c Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Wed, 11 Nov 2026 03:29:23 +0900 Subject: [PATCH 32/34] fix: restore dropped typehint --- dzgui/views/components/buttons.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dzgui/views/components/buttons.py b/dzgui/views/components/buttons.py index 53e89d8..c073059 100644 --- a/dzgui/views/components/buttons.py +++ b/dzgui/views/components/buttons.py @@ -98,7 +98,7 @@ class LargeIconTextButton(IconButton): class ClipboardButton(IconTextButton): - def __init__(self, func: Callable) -> None: + def __init__(self, func: Callable[[], str]) -> None: super().__init__(ICON_CLIPBOARD, atomic_buttons.copy) self.connect("clicked", self._on_button_clicked, func) From c1630adee1c28b80d3efb93fcf681030815a626b Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Wed, 11 Nov 2026 03:31:17 +0900 Subject: [PATCH 33/34] chore: update return value --- dzgui/managers/filter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dzgui/managers/filter.py b/dzgui/managers/filter.py index 5204d35..38aaa7d 100644 --- a/dzgui/managers/filter.py +++ b/dzgui/managers/filter.py @@ -109,7 +109,7 @@ class FilterManager: for m in clean: self.append_map([m]) - def get_all_filters(self) -> tuple: + def get_all_filters(self) -> tuple[str, ...]: map_name = self.get_active_map_name() enabled = self.get_filters() kw = self.get_active_keyword() From 7ae5880890cb12db8dcf894525396fc14a36814e Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Wed, 11 Nov 2026 03:36:14 +0900 Subject: [PATCH 34/34] chore: update union --- dzgui/views/components/buttonbox.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dzgui/views/components/buttonbox.py b/dzgui/views/components/buttonbox.py index deac64c..ea79c45 100644 --- a/dzgui/views/components/buttonbox.py +++ b/dzgui/views/components/buttonbox.py @@ -19,7 +19,11 @@ if TYPE_CHECKING: class ContextualButton(Gtk.Button): def __init__( - self, label: str, opens: "NotebookPage" | None, tooltip: str, context: ButtonType + self, + label: str, + opens: "NotebookPage | None", + tooltip: str, + context: ButtonType, ) -> None: super().__init__( label=label,