Compare commits

...
Author SHA1 Message Date
aclist c267b1bbc7 docs: update changelog
Mirror to Codeberg / mirror-to-codeberg (push) Waiting to run
2026-11-12 02:12:40 +09:00
aclist d0b3a723c6 drop: unused comment 2026-11-12 02:06:36 +09:00
aclist 535bc505f7 chore: update comments 2026-11-12 02:06:20 +09:00
aclist f2375feeec feat: add get_index() method to ServerRow 2026-11-12 02:04:55 +09:00
aclist 1b9448735d chore: drop old comments 2026-11-12 02:03:59 +09:00
aclist d5693b07fb chore: update comments 2026-11-12 02:03:31 +09:00
aclist 7a6f4a2bb9 feat: immutable decorator 2026-11-11 07:52:05 +09:00
aclist ba92d15085 drop: unused signal logic 2026-11-11 07:50:54 +09:00
aclist 65e04c2fc4 fix: multiple signals being stacked 2026-11-11 07:50:38 +09:00
aclist 0907f5a474 change: immutable dataclass 2026-11-11 07:50:12 +09:00
aclist bfa83c3f94 change: import sort order 2026-11-11 07:15:53 +09:00
aclist aef48e515b fix: failing test 2026-11-11 07:15:28 +09:00
aclist e6ebeaeb5e chore: immutable dataclass 2026-11-11 07:04:25 +09:00
aclist 88c740105a chore: drop unused dataclasses 2026-11-11 07:02:23 +09:00
aclist b98a6f95a6 drop: unused imports 2026-11-11 07:00:17 +09:00
aclist 87d76e83ba chore: update DEVELOPERS.md 2026-11-11 06:57:56 +09:00
37 changed files with 146 additions and 178 deletions

No files matched your search

+3 -7
View File
@@ -57,6 +57,7 @@
- List timestamp of last DayZ client update - List timestamp of last DayZ client update
- Validate mods via Mod page to pull in updates - Validate mods via Mod page to pull in updates
- Support for Nasdara (Badlands) DLC - Support for Nasdara (Badlands) DLC
- Load offline mods
### Changed ### Changed
- Conform to PEP 440 versioning for beta versions - Conform to PEP 440 versioning for beta versions
@@ -99,16 +100,11 @@
- Current keyword filter not being applied when refreshing table - Current keyword filter not being applied when refreshing table
- Steam Flatpak cannot run DZGUI as a non-Steam game - Steam Flatpak cannot run DZGUI as a non-Steam game
- Collisions between native Steam and Steam Flatpak clients - Collisions between native Steam and Steam Flatpak clients
- Sticky toggle signal on radio buttons when reloading Options
### Unreleased
- Load offline mods
- Local documentation
- Raw debug command in context menu
### Developer-facing ### Developer-facing
- Add pyproject.toml file
- Show deprecation warnings
- Options -> Dev page - Options -> Dev page
- Raw debug command in preconnect menu
## [6.0.5] 2026-05-17 ## [6.0.5] 2026-05-17
### Changed ### Changed
+6 -5
View File
@@ -5,7 +5,7 @@
### Fetch submodules ### Fetch submodules
git submodule update --recursive --init git submodule update --recursive --init
### Install with dev dependencies ### Install into editable venv with dev dependencies
uv pip install -e '.[dev]' uv pip install -e '.[dev]'
This includes all dependencies for building, linting, testing, and checking in code. This includes all dependencies for building, linting, testing, and checking in code.
@@ -16,7 +16,7 @@ docs: documentation tools
lint: code linting, fixing, and type checking lint: code linting, fixing, and type checking
test: unit and integration tests test: unit and integration tests
## Miscellaneous ## Build process
### Setting up precommit hooks ### Setting up precommit hooks
pre-commit install pre-commit install
@@ -25,6 +25,7 @@ pre-commit install
sphinx-build -M html source build -a sphinx-build -M html source build -a
### Building standalone release binaries ### Building standalone release binaries
1. Set up the build target: `rustup target add x86_64-unknown-linux-musl` 1. Install the applicable system package containing the 'musl-gcc' wrapper
2. Run `python3 scripts/release.py` 2. Set up the build target: `rustup target add x86_64-unknown-linux-musl`
3. Generate a release and tag against the resulting tarfile generated in `dist` 3. Run `python3 scripts/release.py`
4. Generate a release and tag against the resulting tarfile generated in `dist`
-1
View File
@@ -3,7 +3,6 @@ import logging
import re import re
from concurrent.futures import ThreadPoolExecutor, wait from concurrent.futures import ThreadPoolExecutor, wait
from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
+1 -2
View File
@@ -6,7 +6,6 @@ from importlib import resources
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from dzgui.const.constants import ( from dzgui.const.constants import (
APP_NAME, APP_NAME,
APP_NAME_LOWER, APP_NAME_LOWER,
@@ -107,7 +106,7 @@ def load_steam_icon() -> None:
import gi import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk # noqa from gi.repository import Gtk
try: try:
traversable = resources.files(APP_NAME_LOWER).joinpath(ICONS_PATH) traversable = resources.files(APP_NAME_LOWER).joinpath(ICONS_PATH)
+1 -1
View File
@@ -12,7 +12,7 @@ from dzgui.const.constants import (
from dzgui.init.proc import is_sandboxed from dzgui.init.proc import is_sandboxed
@dataclass @dataclass(slots=True, frozen=True)
class Xdg: class Xdg:
config: Path config: Path
columns: Path columns: Path
+3 -9
View File
@@ -1,11 +1,11 @@
import gi
import logging import logging
import threading import threading
from typing import Any, TYPE_CHECKING, Union from typing import Any, TYPE_CHECKING, Union
import dzgui.util._json as JSON # noqa from dzgui.config.userprefs import UserPrefs
from dzgui.const.constants import APP_NAME
from dzgui.const.enum import ( from dzgui.const.enum import (
ButtonType, ButtonType,
ContextMenuItem, ContextMenuItem,
@@ -14,9 +14,6 @@ from dzgui.const.enum import (
Preferences, Preferences,
ServerTab, ServerTab,
) )
from dzgui.config.userprefs import UserPrefs
from dzgui.const.constants import APP_NAME
from dzgui.controllers.emitter import Emitter from dzgui.controllers.emitter import Emitter
from dzgui.managers.config import ConfigManager from dzgui.managers.config import ConfigManager
from dzgui.managers.connection import ConnectionManager from dzgui.managers.connection import ConnectionManager
@@ -29,8 +26,6 @@ from dzgui.util.open_links import open_user_workshop, open_workshop_page
from dzgui.views.dialogs.filepicker import FilePicker from dzgui.views.dialogs.filepicker import FilePicker
from dzgui.views.dialogs.generic import ExceptionDialog from dzgui.views.dialogs.generic import ExceptionDialog
import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk, GLib, GObject from gi.repository import Gtk, Gdk, GLib, GObject
@@ -371,7 +366,6 @@ class Controller(GObject.GObject):
filter_man = self.get_filter_man() filter_man = self.get_filter_man()
return filter_man.get_filters() return filter_man.get_filters()
# FIXME: wipe maps store when changing tabs if model is none
def has_server_model(self) -> bool: def has_server_model(self) -> bool:
treeview = self.get_active_treeview() treeview = self.get_active_treeview()
proxy_man = treeview.get_proxy_man() proxy_man = treeview.get_proxy_man()
+10 -7
View File
@@ -53,6 +53,11 @@
- Expand/collapse changelog versions - Expand/collapse changelog versions
- Support local light/dark mode changes on system theme - Support local light/dark mode changes on system theme
- Server time calculator - Server time calculator
- Add "Unmodded" filter to servers
- List timestamp of last DayZ client update
- Validate mods via Mod page to pull in updates
- Support for Nasdara (Badlands) DLC
- Load offline mods
### Changed ### Changed
- Conform to PEP 440 versioning for beta versions - Conform to PEP 440 versioning for beta versions
@@ -74,6 +79,7 @@
- Enable LAN page Empty/Full filters on first run of app - Enable LAN page Empty/Full filters on first run of app
- Propagate subscribed mods to Steam client - Propagate subscribed mods to Steam client
- Make j/k keys in context menus follow same wraparound logic as down arrow/up arrow - Make j/k keys in context menus follow same wraparound logic as down arrow/up arrow
- Use SQLite DB for geolocation records
### Dropped ### Dropped
- Battlemetrics API support - Battlemetrics API support
@@ -92,16 +98,13 @@
- Moved dialogs out of threads - Moved dialogs out of threads
- History table not being in lock-step with state file - History table not being in lock-step with state file
- Current keyword filter not being applied when refreshing table - Current keyword filter not being applied when refreshing table
- Steam Flatpak cannot run DZGUI as a non-Steam game
### Unreleased - Collisions between native Steam and Steam Flatpak clients
- Load offline mods - Sticky toggle signal on radio buttons when reloading Options
- Local documentation
- Raw debug command in context menu
### Developer-facing ### Developer-facing
- Add pyproject.toml file
- Show deprecation warnings
- Options -> Dev page - Options -> Dev page
- Raw debug command in preconnect menu
## [6.0.5] 2026-05-17 ## [6.0.5] 2026-05-17
### Changed ### Changed
-1
View File
@@ -330,7 +330,6 @@ class ConnectionManager:
self.thread_man.update_dialog(waiting_for_launch) self.thread_man.update_dialog(waiting_for_launch)
while True: while True:
# FIXME: cancel should not be visible here per setting above
if self.controller.is_cancel_pending(): if self.controller.is_cancel_pending():
# TODO: some facility to also close spawned steam process # TODO: some facility to also close spawned steam process
return return
+5
View File
@@ -58,6 +58,11 @@ class ServerRow:
getter = cls._to_tuple getter = cls._to_tuple
return [getter(row) for row in rows] return [getter(row) for row in rows]
@classmethod
def get_index(cls, field: str) -> int:
_fields = [f.name for f in fields(cls)]
return _fields.index(field)
class FiltrationManager: class FiltrationManager:
def __init__(self) -> None: def __init__(self) -> None:
+3 -3
View File
@@ -34,7 +34,7 @@ LAN_TIMEOUT = 0.5
API_TIMEOUT = 3 API_TIMEOUT = 3
@dataclass @dataclass(slots=True, frozen=True)
class NewPlayerCount: class NewPlayerCount:
treeiter: Gtk.TreeIter treeiter: Gtk.TreeIter
players: int players: int
@@ -341,8 +341,8 @@ class ServerModelManager:
return return
config_man.add_saved_server(fqip) config_man.add_saved_server(fqip)
# FIXME: this is valid if saved servers tab is already open, # TODO: this is valid if saved servers tab is already open,
# but not if app was just booted; causes single row to appear prematurely # but not if tab is unloaded; causes single row to appear prematurely
if proxy_man.has_control_model() is False: if proxy_man.has_control_model() is False:
proxy_man.push(records) proxy_man.push(records)
else: else:
+9
View File
@@ -0,0 +1,9 @@
from dataclasses import dataclass
from typing import Callable
def immutable_dataclass(cls: type | None = None, **kwargs: bool) -> type | Callable[[type], type]:
kwargs["slots"] = True
kwargs["frozen"] = True
if cls is None:
return lambda c: dataclass(c, **kwargs)
return dataclass(cls, **kwargs)
+2 -3
View File
@@ -1,10 +1,10 @@
import a2s import a2s
from ipaddress import ip_address
import requests import requests
import shutil import shutil
import subprocess import subprocess
from dataclasses import dataclass from dataclasses import dataclass
from ipaddress import ip_address
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from dzgui.api.servers import Record from dzgui.api.servers import Record
@@ -12,12 +12,11 @@ from dzgui.config.ipdb import read_from_table
from dzgui.const.constants import REQUEST_TIMEOUT from dzgui.const.constants import REQUEST_TIMEOUT
from dzgui.const.endpoints import IP_ECHO from dzgui.const.endpoints import IP_ECHO
if TYPE_CHECKING: if TYPE_CHECKING:
from pathlib import Path from pathlib import Path
@dataclass @dataclass(slots=True, frozen=True)
class Coords: class Coords:
lat: float lat: float
lon: float lon: float
+3 -3
View File
@@ -1,9 +1,9 @@
from typing import Sequence
import gi import gi
from typing import Sequence
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk # noqa from gi.repository import Gtk
class GenericBox(Gtk.Box): class GenericBox(Gtk.Box):
+5 -4
View File
@@ -1,10 +1,11 @@
from typing import Self
from dzgui.util import css
import gi import gi
from typing import Self
from dzgui.util import css
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk # noqa from gi.repository import Gtk
# TODO: subclass Gtk.Frame directly # TODO: subclass Gtk.Frame directly
+2 -3
View File
@@ -1,3 +1,4 @@
import gi
from typing import Self from typing import Self
from warnings import deprecated from warnings import deprecated
@@ -8,10 +9,8 @@ from dzgui.strings import options
from dzgui.views.components.box import HBox from dzgui.views.components.box import HBox
from dzgui.views.components.buttons import Icon from dzgui.views.components.buttons import Icon
import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk # noqa from gi.repository import Gtk
@deprecated("Client choice is determined at setup time") @deprecated("Client choice is determined at setup time")
+3 -2
View File
@@ -106,14 +106,15 @@ class RightPanel(Gtk.Box):
if page.loaded is True: if page.loaded is True:
return return
self.filters_vbox.set_sensitive(False) self.filters_vbox.set_sensitive(False)
# TODO: unless it is lan page self.refresh_button.set_sensitive(False)
# self.refresh_button.set_sensitive(False)
def _on_lan_page_init(self, emitter: "Emitter") -> None: def _on_lan_page_init(self, emitter: "Emitter") -> None:
self.filters_vbox.set_sensitive(False) self.filters_vbox.set_sensitive(False)
def _on_servers_loaded(self, emitter: "Emitter", context: "ServerTab") -> None: def _on_servers_loaded(self, emitter: "Emitter", context: "ServerTab") -> None:
# TODO: similar logic on notebook page change # TODO: similar logic on notebook page change
# TODO: wipe maps store when changing tabs if model is None
state = self.controller.has_server_model() state = self.controller.has_server_model()
self.filters_vbox.set_sensitive(state) self.filters_vbox.set_sensitive(state)
+1 -1
View File
@@ -1,7 +1,7 @@
import gi import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk # noqa from gi.repository import Gtk
class NoOverlayScrolledWindow(Gtk.ScrolledWindow): class NoOverlayScrolledWindow(Gtk.ScrolledWindow):
+1 -1
View File
@@ -101,7 +101,7 @@ class Statusbar(Gtk.Grid):
if type(context) is NotebookPage: if type(context) is NotebookPage:
self.prior_enum = context self.prior_enum = context
self.prior_status = "" self.prior_status = ""
# FIXME: CalcDist is being called when table is not loaded
if dist is None: if dist is None:
self.set_by_context(context, "") self.set_by_context(context, "")
return return
+5 -11
View File
@@ -1,13 +1,9 @@
import gi
import sys import sys
from enum import Enum
from typing import Any, Literal, Self, TYPE_CHECKING from typing import Any, Literal, Self, TYPE_CHECKING
# import time
from enum import Enum
from dzgui.views.mixins.colorscheme import ColorAwareApp
# TODO: import dialog titles
from dzgui.api.mods import remove_stale_signatures as remove_stale from dzgui.api.mods import remove_stale_signatures as remove_stale
from dzgui.config.ipdb import get_ipdb from dzgui.config.ipdb import get_ipdb
from dzgui.const.constants import HEX_GREEN, HEX_RED from dzgui.const.constants import HEX_GREEN, HEX_RED
@@ -20,12 +16,10 @@ from dzgui.util.format import format_exception
from dzgui.util.strings import dialog_header from dzgui.util.strings import dialog_header
from dzgui.util.symlink import rebuild_symlinks from dzgui.util.symlink import rebuild_symlinks
from dzgui.views.components.buttons import ClipboardButton from dzgui.views.components.buttons import ClipboardButton
from dzgui.views.mixins.colorscheme import ColorAwareApp
import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib, Gdk # noqa from gi.repository import Gtk, GLib, Gdk
if TYPE_CHECKING: if TYPE_CHECKING:
from dzgui.config.xdg import Xdg from dzgui.config.xdg import Xdg
+1
View File
@@ -54,6 +54,7 @@ class FolderPicker(Gtk.FileChooserDialog):
return None return None
if res == Gtk.ResponseType.OK: if res == Gtk.ResponseType.OK:
folder = self.get_current_folder() folder = self.get_current_folder()
# TODO: this should never be possible
if folder is None: if folder is None:
try: try:
uri = self.get_uri() uri = self.get_uri()
+1 -1
View File
@@ -15,7 +15,7 @@ from dzgui.views.dialogs.generic import GenericDialog
from dzgui.views.trees.tree_base import TreeView from dzgui.views.trees.tree_base import TreeView
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk, Pango # noqa from gi.repository import Gtk, Gdk, Pango
if TYPE_CHECKING: if TYPE_CHECKING:
from dzgui.api.servers import Details from dzgui.api.servers import Details
+3 -3
View File
@@ -1,3 +1,5 @@
import gi
from typing import Any from typing import Any
from dzgui.const.constants import ( from dzgui.const.constants import (
@@ -7,10 +9,8 @@ from dzgui.const.constants import (
GIO_SETTINGS_VAL, GIO_SETTINGS_VAL,
) )
import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gio # noqa from gi.repository import Gtk, Gio
class ColorAwareApp: class ColorAwareApp:
+1 -1
View File
@@ -1,7 +1,7 @@
import gi import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk # noqa from gi.repository import Gtk, Gdk
class HelpMenuMixin: class HelpMenuMixin:
+3 -3
View File
@@ -1,10 +1,10 @@
import gi
from dzgui.const.enum import VAdjustment from dzgui.const.enum import VAdjustment
from dzgui.const.constants import SCROLL_INCREMENT from dzgui.const.constants import SCROLL_INCREMENT
import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk # noqa from gi.repository import Gtk, Gdk
class ScrollableMixin: class ScrollableMixin:
+2 -3
View File
@@ -1,3 +1,4 @@
import gi
from pathlib import Path from pathlib import Path
from typing import Self, TYPE_CHECKING from typing import Self, TYPE_CHECKING
@@ -8,10 +9,8 @@ from dzgui.views.components.buttons import SteamWorkshopButton
from dzgui.views.components.scrollable import NoOverlayScrolledWindow from dzgui.views.components.scrollable import NoOverlayScrolledWindow
from dzgui.views.trees.tree_mods import ModTreeView from dzgui.views.trees.tree_mods import ModTreeView
import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk # noqa from gi.repository import Gtk
if TYPE_CHECKING: if TYPE_CHECKING:
from dzgui.controllers.mc import Controller from dzgui.controllers.mc import Controller
+6 -7
View File
@@ -1,9 +1,10 @@
from __future__ import annotations from __future__ import annotations
import gi
from enum import Enum from enum import Enum
from typing import Self, TYPE_CHECKING, Union 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 ( from dzgui.const.constants import (
APPID_DAYZ, APPID_DAYZ,
APPID_DAYZ_EXP, APPID_DAYZ_EXP,
@@ -11,8 +12,10 @@ from dzgui.const.constants import (
APPNAME_DAYZ_EXP_HUMAN, APPNAME_DAYZ_EXP_HUMAN,
) )
from dzgui.const.enum import ContextMenuGroup, NotebookPage from dzgui.const.enum import ContextMenuGroup, NotebookPage
from dzgui.const.icons import ICON_EDIT_DELETE, ICON_FOLDER, ICON_WARNING
from dzgui.managers.offline import OfflineManager from dzgui.managers.offline import OfflineManager
from dzgui.strings import generic, offline from dzgui.strings import generic, offline
from dzgui.util import css
from dzgui.views.components.box import HBox, VBox from dzgui.views.components.box import HBox, VBox
from dzgui.views.components.buttons import Icon, IconTextButton from dzgui.views.components.buttons import Icon, IconTextButton
from dzgui.views.components.eventbox import InfoEventBox from dzgui.views.components.eventbox import InfoEventBox
@@ -21,11 +24,8 @@ from dzgui.views.components.scrollable import NoOverlayScrolledWindow
from dzgui.views.components.misc import ErrorPopover from dzgui.views.components.misc import ErrorPopover
from dzgui.views.trees.tree_mods import OfflineModTreeView from dzgui.views.trees.tree_mods import OfflineModTreeView
import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk, GObject # noqa from gi.repository import Gtk, Gdk
if TYPE_CHECKING: if TYPE_CHECKING:
from dzgui.controllers.mc import Controller from dzgui.controllers.mc import Controller
@@ -136,7 +136,6 @@ class FolderHBox(HBox):
self.folder = "" self.folder = ""
self.label.set_label("") self.label.set_label("")
self.unset_button.hide() self.unset_button.hide()
# FIXME: rename signal
self.emitter.emit("custom_mods_unloaded", self) self.emitter.emit("custom_mods_unloaded", self)
def get_button(self) -> Gtk.Button: def get_button(self) -> Gtk.Button:
+14 -17
View File
@@ -1,9 +1,10 @@
import gi
from pathlib import Path from pathlib import Path
from typing import Any, Callable, TYPE_CHECKING from typing import Any, Callable, TYPE_CHECKING
from dzgui.api import pefile as PeFile from dzgui.api import pefile as PeFile
from dzgui.api.steam import get_last_updated from dzgui.api.steam import get_last_updated
from dzgui.config import query from dzgui.config import query
from dzgui.const.constants import ( from dzgui.const.constants import (
APPID_DAYZ, APPID_DAYZ,
@@ -14,12 +15,11 @@ from dzgui.const.constants import (
FLATPAK_SANDBOX, FLATPAK_SANDBOX,
FLATPAK_STEAM_PATH, FLATPAK_STEAM_PATH,
) )
from dzgui.const.icons import ICON_VIEW_CONCEAL, ICON_VIEW_REVEAL
from dzgui.const.endpoints import STEAM_API_SETUP from dzgui.const.endpoints import STEAM_API_SETUP
from dzgui.const.enum import Preferences, ServerTab from dzgui.const.enum import Preferences, ServerTab
from dzgui.const.icons import ICON_VIEW_CONCEAL, ICON_VIEW_REVEAL
from dzgui.strings import options from dzgui.strings import options
from dzgui.util import strings, css, open_links from dzgui.util import strings, css, open_links
from dzgui.views.components.box import ShortHBox, VBox from dzgui.views.components.box import ShortHBox, VBox
from dzgui.views.components.eventbox import InfoEventBox from dzgui.views.components.eventbox import InfoEventBox
from dzgui.views.components.labels import LeftLabel from dzgui.views.components.labels import LeftLabel
@@ -29,11 +29,8 @@ from dzgui.views.components.misc import ErrorPopover
from dzgui.views.dialogs.generic import ExceptionDialog from dzgui.views.dialogs.generic import ExceptionDialog
from dzgui.views.mixins.scrollable_mixin import ScrollableMixin from dzgui.views.mixins.scrollable_mixin import ScrollableMixin
import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk # noqa from gi.repository import Gtk, Gdk
if TYPE_CHECKING: if TYPE_CHECKING:
from dzgui.controllers.mc import Controller from dzgui.controllers.mc import Controller
@@ -233,10 +230,19 @@ class ToggleField(Gtk.Box):
self.pack_start(self.radio1, expand=False, fill=False, padding=0) self.pack_start(self.radio1, expand=False, fill=False, padding=0)
self.pack_start(self.radio2, expand=False, fill=False, padding=0) self.pack_start(self.radio2, expand=False, fill=False, padding=0)
self.signal_id: int | None = None
def disconnect_signal(self) -> None:
if self.signal_id is not None:
self.radio1.disconnect(self.signal_id)
def set_suboption_active(self, state: bool) -> None: def set_suboption_active(self, state: bool) -> None:
# NOTE: defer connection of signal until after state is set # NOTE: defer connection of signal until after state is set
self.radio2.set_active(state) self.radio2.set_active(state)
self.radio1.connect("toggled", self._on_radio_toggled, self.context) self.disconnect_signal()
self.signal_id = self.radio1.connect(
"toggled", self._on_radio_toggled, self.context
)
def _on_radio_toggled(self, button: Gtk.RadioButton, context: Preferences) -> None: def _on_radio_toggled(self, button: Gtk.RadioButton, context: Preferences) -> None:
self.controller.toggle_config(context) self.controller.toggle_config(context)
@@ -486,14 +492,5 @@ class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore
start_tab = self.controller.query_config(Preferences.START_TAB) start_tab = self.controller.query_config(Preferences.START_TAB)
self.start_tab_combo.set_active(start_tab) self.start_tab_combo.set_active(start_tab)
def _suppress_toggles(self, state: bool) -> None:
for toggle in [
self.fullscreen_toggle,
self.distance_toggle,
]:
self.controller.suppress_signal(
self, toggle.get_children()[0], "_on_radio_toggled", state
)
def grab_content_area(self) -> None: def grab_content_area(self) -> None:
self.grab_focus() self.grab_focus()
+3 -20
View File
@@ -1,11 +1,12 @@
from dataclasses import dataclass import gi
from typing import Self, Sequence, TYPE_CHECKING from typing import Self, Sequence, TYPE_CHECKING
from dzgui.const.enum import NotebookPage
from dzgui.const.icons import ( from dzgui.const.icons import (
ICON_ERROR, ICON_ERROR,
ICON_WARNING, ICON_WARNING,
) )
from dzgui.const.enum import NotebookPage
from dzgui.util.css import add_class from dzgui.util.css import add_class
from dzgui.util.keys import is_ctrl_mask from dzgui.util.keys import is_ctrl_mask
from dzgui.util.localize import number from dzgui.util.localize import number
@@ -15,33 +16,15 @@ from dzgui.views.dialogs.generic import DebugDialog
from dzgui.views.components.frame import HeadingFrame from dzgui.views.components.frame import HeadingFrame
from dzgui.views.trees.tree_server_mods import ServerModTreeView from dzgui.views.trees.tree_server_mods import ServerModTreeView
import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gdk, Gtk from gi.repository import Gdk, Gtk
if TYPE_CHECKING: if TYPE_CHECKING:
from dzgui.managers.connection import Prerequisites from dzgui.managers.connection import Prerequisites
from dzgui.controllers.mc import Controller from dzgui.controllers.mc import Controller
from dzgui.controllers.emitter import Emitter from dzgui.controllers.emitter import Emitter
@dataclass
class Warnings:
passworded: bool
dayz_running: bool
no_steam: bool
wrong_version: bool
@dataclass
class Errors:
no_space: bool
no_dayz_exp: bool
no_dayz: bool
class Placeholder(Gtk.Label): class Placeholder(Gtk.Label):
def __init__(self, text: str) -> None: def __init__(self, text: str) -> None:
super().__init__( super().__init__(
+2 -3
View File
@@ -1,3 +1,4 @@
import gi
import logging import logging
from typing import Any, Optional, TYPE_CHECKING from typing import Any, Optional, TYPE_CHECKING
@@ -6,10 +7,8 @@ from dzgui.const.constants import APP_NAME, SEPARATOR
from dzgui.util.keys import is_ctrl_mask, is_navkey from dzgui.util.keys import is_ctrl_mask, is_navkey
from dzgui.views.mixins.cursor_mixin import CursorMixin from dzgui.views.mixins.cursor_mixin import CursorMixin
import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa from gi.repository import Gtk, Gdk, GObject
logger = logging.getLogger(APP_NAME) logger = logging.getLogger(APP_NAME)
+1 -1
View File
@@ -10,7 +10,7 @@ from dzgui.views.trees.tree_base import TreeView
import gi import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa from gi.repository import Gtk
logger = logging.getLogger(APP_NAME) logger = logging.getLogger(APP_NAME)
+3 -3
View File
@@ -1,4 +1,6 @@
import gi
import logging import logging
from typing import Any, TYPE_CHECKING from typing import Any, TYPE_CHECKING
from dzgui.const.constants import APP_NAME, HEX_RED from dzgui.const.constants import APP_NAME, HEX_RED
@@ -9,10 +11,8 @@ from dzgui.views.mixins.context_mixin import ContextMixin
from dzgui.views.mixins.mods_mixin import ModsMixin from dzgui.views.mixins.mods_mixin import ModsMixin
from dzgui.views.trees.tree_base import TreeView from dzgui.views.trees.tree_base import TreeView
import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa from gi.repository import Gtk, Gdk
if TYPE_CHECKING: if TYPE_CHECKING:
+5 -5
View File
@@ -1,15 +1,15 @@
import gi
from typing import TYPE_CHECKING
from dzgui.const.enum import ContextMenuGroup from dzgui.const.enum import ContextMenuGroup
from dzgui.model.model_factory import ModelFactory from dzgui.model.model_factory import ModelFactory
from dzgui.strings import server_mods from dzgui.strings import server_mods
from dzgui.views.mixins.context_mixin import ContextMixin from dzgui.views.mixins.context_mixin import ContextMixin
from dzgui.views.trees.tree_base import TreeView from dzgui.views.trees.tree_base import TreeView
from typing import TYPE_CHECKING
import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib, Gdk, Pango # noqa from gi.repository import Gtk, Pango
if TYPE_CHECKING: if TYPE_CHECKING:
from dzgui.controllers.mc import Controller from dzgui.controllers.mc import Controller
+24 -34
View File
@@ -1,30 +1,25 @@
import gi
import logging import logging
import threading import threading
from queue import Queue from queue import Queue
from typing import Any, Self, Union from typing import Any, Literal, Self, TYPE_CHECKING, Union
from warnings import deprecated from warnings import deprecated
from dzgui.api.servers import ping, Record from dzgui.api.servers import ping, Record
from dzgui.const.constants import APP_NAME from dzgui.const.constants import APP_NAME
from dzgui.const.enum import ContextMenuItem, ContextMenuGroup, ServerTab from dzgui.const.enum import ContextMenuItem, ContextMenuGroup, ServerTab
from dzgui.managers.filter import FilterManager from dzgui.managers.filter import FilterManager
from dzgui.model.proxy_model import ProxyModelManager from dzgui.model.proxy_model import ProxyModelManager, ServerRow
from dzgui.util import strings from dzgui.util import strings
from dzgui.util.dist import CalcDist from dzgui.util.dist import CalcDist
from dzgui.util.keys import is_ctrl_mask from dzgui.util.keys import is_ctrl_mask
from dzgui.views.mixins.context_mixin import ContextMixin from dzgui.views.mixins.context_mixin import ContextMixin
from typing import Literal, TYPE_CHECKING
from dzgui.views.trees.tree_base import TreeView from dzgui.views.trees.tree_base import TreeView
import dzgui.util._json as JSON # noqa import dzgui.util._json as JSON # noqa
import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa from gi.repository import Gtk, GLib, Gdk, GObject
logger = logging.getLogger(APP_NAME) logger = logging.getLogger(APP_NAME)
@@ -34,8 +29,14 @@ if TYPE_CHECKING:
from dzgui.model.model_factory import FastInsertListStore from dzgui.model.model_factory import FastInsertListStore
from dzgui.util.dist import Haversine from dzgui.util.dist import Haversine
# TODO: consts
QUEUE_CHECK_DELAY = 200 QUEUE_CHECK_DELAY = 200
IND_NAME = ServerRow.get_index("name")
IND_ADDR = ServerRow.get_index("ip")
IND_QPORT = ServerRow.get_index("qport")
IND_PING = ServerRow.get_index("ping")
IND_MODDED = ServerRow.get_index("modded")
class ServerTreeView(ContextMixin, TreeView): # type: ignore class ServerTreeView(ContextMixin, TreeView): # type: ignore
def __init__( def __init__(
@@ -145,8 +146,8 @@ class ServerTreeView(ContextMixin, TreeView): # type: ignore
if model is None: if model is None:
return False return False
tree_iter = model.get_iter(path[0]) tree_iter = model.get_iter(path[0])
ip = model.get_value(tree_iter, 7) ip = model.get_value(tree_iter, IND_ADDR)
qport = model.get_value(tree_iter, 8) qport = model.get_value(tree_iter, IND_QPORT)
addr = ip + ":" + str(qport) addr = ip + ":" + str(qport)
note = self.controller.get_note_by_record(addr) note = self.controller.get_note_by_record(addr)
@@ -169,14 +170,12 @@ class ServerTreeView(ContextMixin, TreeView): # type: ignore
return self.enum return self.enum
def _on_map(self, widget: Self) -> None: def _on_map(self, widget: Self) -> None:
# TODO: disable filter panel if current model is None
state = self.get_enum() is ServerTab.LAN state = self.get_enum() is ServerTab.LAN
self.emitter.emit("lan_tab_toggled", state) self.emitter.emit("lan_tab_toggled", state)
store = self.filter_man.get_map_store() store = self.filter_man.get_map_store()
# FIXME: if model is none, wipe maps # TODO: distinguish this signal from changing map combo in-situ
# distinguish this signal from changing map combo in-situ
self.emitter.emit("load_maps", store) self.emitter.emit("load_maps", store)
self.handler_id = self.emitter.connect("statusbar_loaded", self.start_distcalc) self.handler_id = self.emitter.connect("statusbar_loaded", self.start_distcalc)
self.start_queue_checker() self.start_queue_checker()
@@ -221,7 +220,7 @@ class ServerTreeView(ContextMixin, TreeView): # type: ignore
def _check_result_queue(self) -> Literal[True]: def _check_result_queue(self) -> Literal[True]:
latest_result = None latest_result = None
# FIXME: empty results cause statusbar to be emptied # TODO: Nonetype results cause statusbar to be blank
while not self.queue.empty(): while not self.queue.empty():
latest_result = self.queue.get() latest_result = self.queue.get()
@@ -267,15 +266,9 @@ class ServerTreeView(ContextMixin, TreeView): # type: ignore
(model, pathlist) = sels (model, pathlist) = sels
path = pathlist[0] path = pathlist[0]
tree_iter = model.get_iter(path) tree_iter = model.get_iter(path)
has_mods = model.get_value(tree_iter, 11) has_mods = model.get_value(tree_iter, IND_MODDED)
return bool(has_mods) return bool(has_mods)
# def get_selected_row(self) -> Gtk.TreeModelRow:
# sel = self.get_selection()
# sels = sel.get_selected_rows()
# print(type(sels[0]))
# return sels[0]
def is_in_favs(self) -> bool: def is_in_favs(self) -> bool:
record = self.get_record_string() record = self.get_record_string()
if self.controller.get_config_man().is_in_favs(record): if self.controller.get_config_man().is_in_favs(record):
@@ -304,17 +297,17 @@ class ServerTreeView(ContextMixin, TreeView): # type: ignore
self.start_distcalc() self.start_distcalc()
def get_name(self) -> str: def get_name(self) -> str:
return str(self.get_value_at_index(0)) return str(self.get_value_at_index(IND_NAME))
def get_simplified_ip(self) -> str: def get_simplified_ip(self) -> str:
addr = self.get_value_at_index(7) addr = self.get_value_at_index(IND_ADDR)
qport = self.get_value_at_index(8) qport = self.get_value_at_index(IND_QPORT)
ip = addr.split(":")[0] ip = addr.split(":")[0]
return f"{ip}:{qport}" return f"{ip}:{qport}"
def get_record_string(self) -> str: def get_record_string(self) -> str:
addr = self.get_value_at_index(7) addr = self.get_value_at_index(IND_ADDR)
qport = self.get_value_at_index(8) qport = self.get_value_at_index(IND_QPORT)
return f"{addr}:{qport}" return f"{addr}:{qport}"
def get_record(self) -> Record | None: def get_record(self) -> Record | None:
@@ -353,8 +346,7 @@ class ServerTreeView(ContextMixin, TreeView): # type: ignore
def _redraw( def _redraw(
self, model: "FastInsertListStore", _iter: Gtk.TreeIter, _ping: int self, model: "FastInsertListStore", _iter: Gtk.TreeIter, _ping: int
) -> None: ) -> None:
ping_column = 9 model.set(_iter, IND_PING, _ping)
model.set(_iter, ping_column, _ping)
self.queue_draw() self.queue_draw()
def wipe_cache(self) -> None: def wipe_cache(self) -> None:
@@ -369,13 +361,10 @@ class ServerTreeView(ContextMixin, TreeView): # type: ignore
data: Any, data: Any,
) -> None: ) -> None:
addr_column = 7 addr = model.get_value(_iter, IND_ADDR).split(":")
qport_column = 8
addr = model.get_value(_iter, addr_column).split(":")
ip = addr[0] ip = addr[0]
gameport = int(addr[1]) gameport = int(addr[1])
qport = model.get_value(_iter, qport_column) qport = model.get_value(_iter, IND_QPORT)
record = Record(ip, gameport, qport) record = Record(ip, gameport, qport)
if record in self.seen_cache: if record in self.seen_cache:
@@ -415,6 +404,7 @@ class ServerTreeView(ContextMixin, TreeView): # type: ignore
real_model = self.proxy_man.get_control() real_model = self.proxy_man.get_control()
from dataclasses import fields from dataclasses import fields
from dzgui.model.proxy_model import ServerRow from dzgui.model.proxy_model import ServerRow
key = fields(ServerRow)[col_index].name key = fields(ServerRow)[col_index].name
value = getattr(real_model[row_index], key) value = getattr(real_model[row_index], key)
+5 -5
View File
@@ -1,13 +1,13 @@
import gi
from typing import TYPE_CHECKING, Union from typing import TYPE_CHECKING, Union
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
if TYPE_CHECKING: if TYPE_CHECKING:
from dzgui.model.model_factory import FastInsertListStore from dzgui.model.model_factory import FastInsertListStore
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk # noqa
class SimpleTree(Gtk.ScrolledWindow): class SimpleTree(Gtk.ScrolledWindow):
"""Simple scrollable 1-column tree""" """Simple scrollable 1-column tree"""
+2 -3
View File
@@ -1,13 +1,12 @@
import gi
import pytest import pytest
import subprocess import subprocess
from dzgui.const.constants import GIO_SETTINGS_KEY from dzgui.const.constants import GIO_SETTINGS_KEY
from dzgui.views.mixins.colorscheme import ColorAwareApp from dzgui.views.mixins.colorscheme import ColorAwareApp
import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gio, GLib # noqa from gi.repository import Gtk, GLib
class DummyWidget(ColorAwareApp, Gtk.Button): class DummyWidget(ColorAwareApp, Gtk.Button):
+2 -2
View File
@@ -48,5 +48,5 @@ def test_signatures_with_no_ids(monkeypatch, tmp: str, versions: str) -> None:
monkeypatch.setattr("dzgui.api.mods.lookup", mock_lookup) monkeypatch.setattr("dzgui.api.mods.lookup", mock_lookup)
dzgui.api.mods.remove_stale_signatures(path, path) dzgui.api.mods.remove_stale_signatures(path, path)
with open(tmp, "r") as f: with open(tmp, "r") as f:
lines = f.readlines() lines = [line.rstrip() for line in f.readlines()]
assert lines == ["3410710885,1752864732\n", "3739934289,1780855136\n"] assert lines == ["3410710885,1752864732", "3739934289,1780855136"]
+5 -3
View File
@@ -7,7 +7,7 @@ from dzgui.api.steam import (
VDFLoadError, VDFLoadError,
AppNotInstalledError, AppNotInstalledError,
AppMovedError, AppMovedError,
get_app_path get_app_path,
) )
from dzgui.config.query import get_config from dzgui.config.query import get_config
@@ -26,9 +26,8 @@ def default_steam_path():
conf = get_config(xdg.config) conf = get_config(xdg.config)
return conf["default_steam_path"] return conf["default_steam_path"]
# TODO: additional parsing tests
# TODO: split into separate test file
# FIXME: use fixtures
def test_pefile_length(default_steam_path): def test_pefile_length(default_steam_path):
try: try:
pe_file_path = PeFile.get_pefile_path(Path(default_steam_path), APPID_DAYZ) pe_file_path = PeFile.get_pefile_path(Path(default_steam_path), APPID_DAYZ)
@@ -47,6 +46,7 @@ def test_invalid_pefile_path():
raise e raise e
# TODO: split into separate test file
@pytest.mark.parametrize( @pytest.mark.parametrize(
"fixture, exception", "fixture, exception",
[ [
@@ -57,6 +57,7 @@ def test_invalid_pefile_path():
) )
def test_not_in_library(monkeypatch, fixture, exception): def test_not_in_library(monkeypatch, fixture, exception):
import dzgui.api.steam as steam import dzgui.api.steam as steam
monkeypatch.setattr(steam, "LIBRARYFOLDERS_PATH", fixture) monkeypatch.setattr(steam, "LIBRARYFOLDERS_PATH", fixture)
folder_path = Path(fixture_path("api")) folder_path = Path(fixture_path("api"))
@@ -74,6 +75,7 @@ def second_drive():
def test_on_second_drive(monkeypatch, second_drive): def test_on_second_drive(monkeypatch, second_drive):
import dzgui.api.steam as steam import dzgui.api.steam as steam
monkeypatch.setattr(steam, "LIBRARYFOLDERS_PATH", second_drive) monkeypatch.setattr(steam, "LIBRARYFOLDERS_PATH", second_drive)
folder_path = Path(fixture_path("api")) folder_path = Path(fixture_path("api"))
path = get_app_path(folder_path, APPID_DAYZ) path = get_app_path(folder_path, APPID_DAYZ)