Compare commits

...
31 Commits
Author SHA1 Message Date
aclist 1154789f8b Merge pull request #389 from aclist/fix/defer-import
Mirror to Codeberg / mirror-to-codeberg (push) Waiting to run
fix: defer flock action until GUI is loaded
2026-06-25 00:05:27 +09:00
aclist cbccd4c5f6 fix: defer flock action until GUI is loaded 2026-06-25 00:04:18 +09:00
aclist 8cac752a0d Merge pull request #388 from aclist/prerelease/7.0.0b14
Mirror to Codeberg / mirror-to-codeberg (push) Waiting to run
chore: bump version
2026-06-24 22:07:38 +09:00
aclist 580273053c chore: bump version 2026-06-24 21:43:45 +09:00
aclist 8d75c1f7b2 Merge pull request #386 from aclist/feat/libgi-preboot
Mirror to Codeberg / mirror-to-codeberg (push) Waiting to run
feat: libgi preboot test
2026-06-24 01:06:51 +09:00
aclist 320c2f692e chore: more explicit library name 2026-06-24 01:05:12 +09:00
aclist 413d84b2b5 chore: reword comment 2026-06-24 01:04:07 +09:00
aclist c751b277d0 fix: unhandled exception 2026-06-24 00:59:56 +09:00
aclist 7f36a3acad fix: mislabeled keybindings 2026-06-23 23:02:39 +09:00
aclist 5780d3daff feat: abort gracefully if libgirepository is missing 2026-06-23 23:02:18 +09:00
aclist e692e1d139 Merge pull request #385 from aclist/fix/maps-combo-unloaded
Mirror to Codeberg / mirror-to-codeberg (push) Waiting to run
fix: maps combo not updating on unloaded model
2026-06-23 19:30:36 +09:00
aclist 38d44a7952 chore: drop unused string 2026-06-23 19:29:23 +09:00
aclist 611e3e6e58 fix: drop extraneous chained thread 2026-06-23 19:26:57 +09:00
aclist d5b415316e fix: update dialog string 2026-06-23 19:17:47 +09:00
aclist 0eeec490c2 chore: clear typehinting errors 2026-06-23 19:13:15 +09:00
aclist 782157006a fix: regression introduced in #382 2026-06-23 19:12:30 +09:00
aclist 3be7bc0e02 fix: reinit map store on empty model (#359) 2026-06-23 19:12:06 +09:00
aclist 9e7102aaba fix: wipe cache on refresh action (#384) 2026-06-23 19:11:30 +09:00
aclist 6516e7ae3c Merge pull request #383 from aclist/fix/block-refresh
fix: do not trigger decrement if tree is unloaded
2026-06-23 18:29:55 +09:00
aclist 46c1da232e fix: do not trigger decrement if tree is unloaded (#360) 2026-06-23 18:27:34 +09:00
aclist 5280121e43 Merge pull request #382 from aclist/fix/needs-update-column
fix: use unified mod list logic
2026-06-23 18:26:45 +09:00
aclist 17c63cf85f chore: drop unused code 2026-06-23 18:26:18 +09:00
aclist 488d98800f chore: clear typehinting errors 2026-06-23 18:14:37 +09:00
aclist 4eac5055cb fix: use unified logic for modlist queries (#354) 2026-06-23 18:09:57 +09:00
aclist 81168d3952 Merge pull request #381 from aclist/fix/modpanel-sens
Mirror to Codeberg / mirror-to-codeberg (push) Waiting to run
fix: toggle mod panel sensitivity on map
2026-06-23 00:37:40 +09:00
aclist ee90be42ee fix: toggle mod panel sensitivity on map 2026-06-23 00:18:02 +09:00
aclist 48f341bae2 Merge pull request #379 from aclist/fix/synch-icons
fix: synch icons
2026-06-22 22:59:38 +09:00
aclist 0653f064b2 chore: add tests for mod signatures
Mirror to Codeberg / mirror-to-codeberg (push) Has been cancelled
2026-06-22 22:55:44 +09:00
aclist 525dc24508 fix: missing import 2026-06-22 17:02:39 +09:00
aclist 4e7c831a53 fix: pass mods and iters to callback 2026-06-22 17:02:27 +09:00
aclist e41bef7b00 fix: use list comprehension 2026-06-22 17:01:45 +09:00
18 changed files with 254 additions and 144 deletions

No files matched your search

+17 -7
View File
@@ -49,6 +49,7 @@ def get_local_mods(workshop_path: Path) -> list[Path]:
mods = [file for file in workshop_path.iterdir() if file.is_dir()] mods = [file for file in workshop_path.iterdir() if file.is_dir()]
return mods return mods
def is_mission(path: Path) -> bool: def is_mission(path: Path) -> bool:
# TODO: parse integrity of other files # TODO: parse integrity of other files
parent = path.parent.name parent = path.parent.name
@@ -57,9 +58,10 @@ def is_mission(path: Path) -> bool:
file = path / "init.c" file = path / "init.c"
return file.exists() return file.exists()
def tokenize(mod: Path) -> dict[str, Any] | None: def tokenize(mod: Path) -> dict[str, Any] | None:
file = mod.joinpath("meta.cpp") file = mod.joinpath("meta.cpp")
delimiter=r"\s*=\s*" delimiter = r"\s*=\s*"
modmeta = {} modmeta = {}
try: try:
with open(file, "r", encoding="utf-8") as f: with open(file, "r", encoding="utf-8") as f:
@@ -76,6 +78,7 @@ def tokenize(mod: Path) -> dict[str, Any] | None:
logger.critical(e) logger.critical(e)
return None return None
def get_mod_size(path: Path) -> float: def get_mod_size(path: Path) -> float:
s = 0 s = 0
for f in path.rglob("*"): for f in path.rglob("*"):
@@ -123,20 +126,27 @@ def _hash(uid: str, use_custom: bool = False) -> str:
return prefix + md5.hexdigest()[:8] return prefix + md5.hexdigest()[:8]
def remove_stale_signatures(config: Path, versions: Path) -> None: def remove_stale_signatures(
config: Path, versions: Path, ids: list[int] | None = None
) -> None:
if versions.is_file() is False: if versions.is_file() is False:
logger.warning("Creating new version signatures file") logger.warning("Creating new version signatures file")
versions.touch() versions.touch()
return return
path = lookup(config, Preferences.DEFAULT) path = lookup(config, Preferences.DEFAULT)
steam_path = Path(path) steam_path = Path(path)
ids = get_local_mod_ids(steam_path) if ids is None:
local_ids = get_local_mod_ids(steam_path)
with open(versions, "r") as f: with open(versions, "r") as f:
lines = f.readlines() lines = f.readlines()
for line in lines:
uid = int(line.split(",")[0]) if ids is None:
if uid not in ids: lines = [line for line in lines if int(line.split(",")[0]) in local_ids]
lines.remove(line) else:
lines = [line for line in lines if int(line.split(",")[0]) not in ids]
with open(versions, "w") as f: with open(versions, "w") as f:
for line in lines: for line in lines:
f.write(line) f.write(line)
+2 -1
View File
@@ -289,13 +289,14 @@ def query_direct(ip: str, qport: int, timeout: float = 3.0) -> dict[str, Any] |
def get_details(record: Record) -> Details: def get_details(record: Record) -> Details:
# TODO: consolidate try/except statements
ip = record.ip ip = record.ip
qport = record.qport qport = record.qport
default_str = strings.none_provided default_str = strings.none_provided
try: try:
info = a2s.info((ip, qport)) info = a2s.info((ip, qport))
except TimeoutError: except (TimeoutError, a2s.exceptions.BrokenMessageError):
return Details(None, default_str, False) return Details(None, default_str, False)
try: try:
rules = dayzquery.dayz_rules((ip, qport)) rules = dayzquery.dayz_rules((ip, qport))
+109
View File
@@ -0,0 +1,109 @@
import logging
import os
from typing import TYPE_CHECKING
from dzgui.const.constants import APP_NAME
from dzgui.const.enum import Preferences
from dzgui.config.query import lookup
from dzgui.config.userprefs import UserPrefs
from dzgui.config.xdg import get_xdg_paths, parse_filepaths
from dzgui.init.migrate import (
has_new_config,
copy_state_files,
migrate_cols_file,
)
from dzgui.init.flock import lock_acquire
from dzgui.init.prereqs import has_steam_client
from dzgui.strings import boot
# from dzgui.util.map_count import get_map_count
from dzgui.util.deck import is_steam_deck, is_game_mode
from dzgui.util.localize import set_locale
from dzgui.util.strings import init
from dzgui.views.base import App
from dzgui.views.dialogs.boot import BootWindow
from dzgui.views.dialogs.early_alert import EarlyAlertDialog
from dzgui.views.dialogs.wizard import SetupWizard
if TYPE_CHECKING:
from pathlib import Path
logger = logging.getLogger(APP_NAME)
# TODO: profile load time
def make_parents(path: "Path") -> None:
path.parent.mkdir(parents=True, exist_ok=True)
def setup_logger(log_path: "Path") -> None:
_format = (
"%(asctime)s␞%(levelname)s␞%(filename)s::%(funcName)s::%(lineno)s␞%(message)s"
)
fh = logging.FileHandler(log_path)
formatter = logging.Formatter(_format)
fh.setFormatter(formatter)
fh.setLevel(logging.DEBUG)
logger.setLevel(logging.DEBUG)
logger.addHandler(fh)
def load_gui(version: str, is_debug: bool) -> None:
lock = lock_acquire() # noqa
set_locale()
# NOTE: consider aborting this check if steam deck
xdg_paths = get_xdg_paths()
XDG = parse_filepaths(xdg_paths)
if XDG.resolution.parent.is_dir() is False:
make_parents(XDG.resolution)
if XDG.debug.is_file() is False:
make_parents(XDG.debug)
_is_steam_deck = is_steam_deck()
_is_game_mode = is_game_mode() if _is_steam_deck else False
if _is_game_mode:
# NOTE: this may no longer be necessary on newer versions of SteamOS
del os.environ["GTK_IM_MODULE"]
if has_new_config(XDG.config) is False:
migrate_cols_file(XDG.columns)
copy_state_files(xdg_paths["XDG_STATE_HOME"])
# TODO: add logging inside wizard
SetupWizard(_is_steam_deck, XDG.config)
# NOTE: implies that setup wizard failed or was closed
if has_new_config(XDG.config) is False:
return
setup_logger(XDG.debug)
with open(XDG.debug, "w") as f:
f.truncate(0)
if _is_steam_deck is False:
# TODO: sudo escalation dialog
# count = get_map_count()
# TODO: move into module
if has_steam_client() is False:
EarlyAlertDialog(init.requires_steam)
bootwin = BootWindow(XDG, version)
local_coords, latest_release = bootwin.get_results()
use_miles = lookup(XDG.config, Preferences.DIST)
prefs = UserPrefs(
is_steam_deck=_is_steam_deck,
is_game_mode=_is_game_mode,
is_debug=is_debug,
coords=local_coords,
version=version,
paths=XDG,
latest_release=latest_release,
use_miles=use_miles,
)
print(boot.all_ok)
App(prefs)
+2 -2
View File
@@ -81,8 +81,8 @@ class Emitter(GObject.GObject):
def distcalc_started(self) -> None: def distcalc_started(self) -> None:
pass pass
@GObject.Signal(flags=GObject.SignalFlags.RUN_LAST, arg_types=(bool,)) @GObject.Signal(flags=GObject.SignalFlags.RUN_LAST, arg_types=(bool, int))
def mod_page_toggled(self, state: bool) -> None: def mod_page_toggled(self, state: bool, mods: int) -> None:
pass pass
@GObject.Signal(flags=GObject.SignalFlags.RUN_LAST, arg_types=(bool,)) @GObject.Signal(flags=GObject.SignalFlags.RUN_LAST, arg_types=(bool,))
+1 -1
View File
@@ -44,7 +44,7 @@ def has_libgi() -> bool:
return exists return exists
def test_missing_lib() -> bool: def test_libgi_missing() -> None:
msg = f"System is missing the required library '{LIB}'. Install it via your system package manager." msg = f"System is missing the required library '{LIB}'. Install it via your system package manager."
if has_libgi() is False: if has_libgi() is False:
print(msg) print(msg)
+1
View File
@@ -1,6 +1,7 @@
import psutil import psutil
import subprocess import subprocess
import shutil import shutil
import logging
from warnings import deprecated from warnings import deprecated
+6 -103
View File
@@ -1,40 +1,11 @@
import argparse import argparse
import logging
import os
import sys import sys
import warnings import warnings
from typing import TYPE_CHECKING
from dzgui.const.constants import APP_NAME from dzgui.const.constants import APP_NAME
from dzgui.const.enum import Preferences from dzgui.init.libgi import test_libgi_missing
from dzgui.config.query import lookup
from dzgui.config.userprefs import UserPrefs
from dzgui.config.xdg import get_xdg_paths, parse_filepaths
from dzgui.init.flock import lock_acquire
from dzgui.init.migrate import (
has_new_config,
copy_state_files,
migrate_cols_file,
)
from dzgui.init.prefix import get_version from dzgui.init.prefix import get_version
from dzgui.init.prereqs import has_steam_client from dzgui.util.strings import flags
from dzgui.strings import boot
# from dzgui.util.map_count import get_map_count
from dzgui.util.deck import is_steam_deck, is_game_mode
from dzgui.util.localize import set_locale
from dzgui.util.strings import init, flags
from dzgui.views.base import App
from dzgui.views.dialogs.boot import BootWindow
from dzgui.views.dialogs.early_alert import EarlyAlertDialog
from dzgui.views.dialogs.wizard import SetupWizard
if TYPE_CHECKING:
from pathlib import Path
logger = logging.getLogger(APP_NAME)
parser = argparse.ArgumentParser(description=flags.description) parser = argparse.ArgumentParser(description=flags.description)
parser.add_argument("-v", "--version", action="store_true", help=flags.version) parser.add_argument("-v", "--version", action="store_true", help=flags.version)
@@ -43,11 +14,6 @@ parser.add_argument("-d", "--debug", action="store_true", help=flags.debug)
args = parser.parse_args() args = parser.parse_args()
# TODO: profile load time
def make_parents(path: "Path") -> None:
path.parent.mkdir(parents=True, exist_ok=True)
def uninstall() -> None: def uninstall() -> None:
# TODO: uninstall data files (-u) # TODO: uninstall data files (-u)
# -u removes state, log, freedesktop # -u removes state, log, freedesktop
@@ -57,21 +23,7 @@ def uninstall() -> None:
pass pass
def setup_logger(log_path: "Path") -> None:
_format = (
"%(asctime)s␞%(levelname)s␞%(filename)s::%(funcName)s::%(lineno)s␞%(message)s"
)
fh = logging.FileHandler(log_path)
formatter = logging.Formatter(_format)
fh.setFormatter(formatter)
fh.setLevel(logging.DEBUG)
logger.setLevel(logging.DEBUG)
logger.addHandler(fh)
def main() -> None: def main() -> None:
lock = lock_acquire() # noqa
if args.version is True: if args.version is True:
print(get_version()) print(get_version())
sys.exit(0) sys.exit(0)
@@ -82,60 +34,11 @@ def main() -> None:
warnings.filterwarnings("default", category=DeprecationWarning) warnings.filterwarnings("default", category=DeprecationWarning)
version = get_version() version = get_version()
set_locale()
print(f"{APP_NAME} {version}") print(f"{APP_NAME} {version}")
test_libgi_missing()
# NOTE: consider aborting this check if steam deck # NOTE: only import ligbirepository modules after the above check
xdg_paths = get_xdg_paths() from dzgui.app_init import load_gui
XDG = parse_filepaths(xdg_paths)
if XDG.resolution.parent.is_dir() is False: load_gui(version, args.debug)
make_parents(XDG.resolution)
if XDG.debug.is_file() is False:
make_parents(XDG.debug)
_is_steam_deck = is_steam_deck()
_is_game_mode = is_game_mode() if _is_steam_deck else False
if _is_game_mode:
# NOTE: this may no longer be necessary on newer versions of SteamOS
del os.environ["GTK_IM_MODULE"]
if has_new_config(XDG.config) is False:
migrate_cols_file(XDG.columns)
copy_state_files(xdg_paths["XDG_STATE_HOME"])
# TODO: add logging inside wizard
SetupWizard(_is_steam_deck, XDG.config)
# NOTE: implies that setup wizard failed or was closed
if has_new_config(XDG.config) is False:
return
setup_logger(XDG.debug)
with open(XDG.debug, "w") as f:
f.truncate(0)
if _is_steam_deck is False:
# TODO: sudo escalation dialog
# count = get_map_count()
# TODO: move into module
if has_steam_client() is False:
EarlyAlertDialog(init.requires_steam)
bootwin = BootWindow(XDG, version)
local_coords, latest_release = bootwin.get_results()
use_miles = lookup(XDG.config, Preferences.DIST)
prefs = UserPrefs(
is_steam_deck=_is_steam_deck,
is_game_mode=_is_game_mode,
is_debug=args.debug,
coords=local_coords,
version=version,
paths=XDG,
latest_release=latest_release,
use_miles=use_miles,
)
print(boot.all_ok)
App(prefs)
+24 -13
View File
@@ -35,7 +35,11 @@ from dzgui.const.constants import (
from dzgui.const.enum import NotebookPage, Preferences from dzgui.const.enum import NotebookPage, Preferences
from dzgui.init.proc import is_dayz_running, is_steam_running from dzgui.init.proc import is_dayz_running, is_steam_running
from dzgui.managers.threading import call_on_thread, StoredFunc, ThreadingManager from dzgui.managers.threading import call_on_thread, StoredFunc, ThreadingManager
from dzgui.strings.dialogs import waiting_for_launch, waiting_for_mods, waiting_for_directories from dzgui.strings.dialogs import (
waiting_for_launch,
waiting_for_mods,
waiting_for_directories,
)
from dzgui.strings.server_mods import checkmark, resync from dzgui.strings.server_mods import checkmark, resync
from dzgui.util.format import format_mib from dzgui.util.format import format_mib
from dzgui.util.strings import dialog, server_timeout from dzgui.util.strings import dialog, server_timeout
@@ -144,21 +148,14 @@ class ConnectionManager:
remote_mods: list[list[str]] = [] remote_mods: list[list[str]] = []
if res.is_modded(): if res.is_modded():
try: try:
remote_mods = self._query_modlist(record) remote_mods, missing_mods = self._query_modlist(record)
self.missing_mods = missing_mods
self.remote_mod_ids = [mod[1] for mod in remote_mods] self.remote_mod_ids = [mod[1] for mod in remote_mods]
except Exception as e: except Exception as e:
logger.warning(e) logger.warning(e)
self.thread_man.set_cleanup_func(failure_func, destroy_first=True) self.thread_man.set_cleanup_func(failure_func, destroy_first=True)
return return
hashes = get_remote_signatures(self.remote_mod_ids)
version_file = prefs.paths.version
self.missing_mods = get_needs_update(version_file, hashes)
for mod in remote_mods:
if any(mod[1] in tuple for tuple in self.missing_mods):
mod[2] = resync
if local_version is not None: if local_version is not None:
pefile_path = PeFile.get_pefile_path(steam_path, info.game_id) pefile_path = PeFile.get_pefile_path(steam_path, info.game_id)
total, used, free = shutil.disk_usage(pefile_path) total, used, free = shutil.disk_usage(pefile_path)
@@ -222,10 +219,13 @@ class ConnectionManager:
StoredFunc(self._present_details_dialog, details), destroy_first=True StoredFunc(self._present_details_dialog, details), destroy_first=True
) )
def _query_modlist(self, record: Servers.Record) -> list[list[str]]: def _query_modlist(
self, record: Servers.Record
) -> tuple[list[list[str]], list[tuple[str, str, int, int]]]:
mods = Servers.get_rules(record) mods = Servers.get_rules(record)
steam_path = self.controller.query_config(Preferences.DEFAULT) steam_path = self.controller.query_config(Preferences.DEFAULT)
local = get_local_mod_ids(steam_path) local = get_local_mod_ids(steam_path)
alpha_mods: list[list[str]] = [ alpha_mods: list[list[str]] = [
[ [
mod.name, mod.name,
@@ -235,12 +235,23 @@ class ConnectionManager:
for mod in mods for mod in mods
] ]
alpha_mods.sort(key=lambda x: x[0]) alpha_mods.sort(key=lambda x: x[0])
return alpha_mods
prefs = self.controller.get_prefs()
version_file = prefs.paths.version
remote_mod_ids = [mod[1] for mod in alpha_mods]
hashes = get_remote_signatures(remote_mod_ids)
missing_mods = get_needs_update(version_file, hashes)
for mod in alpha_mods:
if any(mod[1] in tuple for tuple in missing_mods):
mod[2] = resync
return alpha_mods, missing_mods
@call_on_thread(dialog.querying) @call_on_thread(dialog.querying)
def query_modlist_and_present(self, record: Servers.Record) -> None: def query_modlist_and_present(self, record: Servers.Record) -> None:
try: try:
mods = self._query_modlist(record) mods, missing_mods = self._query_modlist(record)
except Exception: except Exception:
self.thread_man.set_cleanup_func( self.thread_man.set_cleanup_func(
StoredFunc(self._server_timeout), destroy_first=True StoredFunc(self._server_timeout), destroy_first=True
+1
View File
@@ -80,6 +80,7 @@ class FilterManager:
if maps is None: if maps is None:
return return
if len(maps) < 1: if len(maps) < 1:
self.reinit_map_store()
return return
self.reinit_map_store() self.reinit_map_store()
+8 -6
View File
@@ -118,8 +118,7 @@ class ModManager:
self.thread_man.increment_dialog() self.thread_man.increment_dialog()
time.sleep(API_RATE_LIMIT) time.sleep(API_RATE_LIMIT)
iters = [_iter for mod, _iter in mods] func = StoredFunc(self._on_mods_unsubbed, mods)
func = StoredFunc(self._on_mods_unsubbed, iters)
self.thread_man.set_cleanup_func(func) self.thread_man.set_cleanup_func(func)
def unsub_atomic_mod(self, mod: str) -> None: def unsub_atomic_mod(self, mod: str) -> None:
@@ -146,18 +145,21 @@ class ModManager:
pass pass
time.sleep(API_RATE_LIMIT) time.sleep(API_RATE_LIMIT)
def _on_mods_unsubbed(self, iters: list[Gtk.TreeIter]) -> None: def _on_mods_unsubbed(self, mod_iter: list[tuple[str, Gtk.TreeIter]]) -> None:
mods = [int(mod) for mod, _iter in mod_iter]
iters = [_iter for mod, _iter in mod_iter]
if self.store is None: if self.store is None:
return return
for _iter in iters: for _iter in iters:
self.store.remove(_iter) self.store.remove(_iter)
remove_stale_signatures(self.prefs.paths.config, self.prefs.paths.version) # TODO: process config path in called function
remove_stale_signatures(self.prefs.paths.config, self.prefs.paths.version, mods)
model = self.treeview.get_model() model = self.treeview.get_model()
if model is None: if model is None:
return return
mods = len(model) total_mods = len(model)
msg = self.format_mod_statusbar() msg = self.format_mod_statusbar()
self.emitter.emit("mods_updated", msg, mods) self.emitter.emit("mods_updated", msg, total_mods)
def uncolorize_mods(self) -> None: def uncolorize_mods(self) -> None:
model = self.treeview.get_model() model = self.treeview.get_model()
+3 -1
View File
@@ -395,8 +395,10 @@ class ServerModelManager:
self.first_iteration = False self.first_iteration = False
def _cleanup_on_success(self) -> None: def _cleanup_on_success(self) -> None:
proxy = self._get_proxy_man().get_proxy_model() proxy = self.proxy_man.get_proxy_model()
self.tv.set_model(proxy) self.tv.set_model(proxy)
if self.preserve_on_fail is True:
self.proxy_man.wipe_cache()
self.emitter.emit("servers_loaded", self.enum) self.emitter.emit("servers_loaded", self.enum)
if self.first_iteration: if self.first_iteration:
+2 -3
View File
@@ -130,7 +130,6 @@ servers = {
"Enter/space/double-click": "connect to server", "Enter/space/double-click": "connect to server",
"Right-click/Ctrl-l": "additional context menus", "Right-click/Ctrl-l": "additional context menus",
"Ctrl-r": "refresh players", "Ctrl-r": "refresh players",
"Ctrl-p": "refresh ping",
"Ctrl-f": "jump to keyword search field", "Ctrl-f": "jump to keyword search field",
"Ctrl-m": "jump to maps field", "Ctrl-m": "jump to maps field",
"Ctrl-d": "select default LAN port", "Ctrl-d": "select default LAN port",
@@ -148,8 +147,8 @@ servers = {
vim = { vim = {
"j": "Move down a row/scroll up", "j": "Move down a row/scroll up",
"k": "Move up a row/scroll down", "k": "Move up a row/scroll down",
"l": "Jump to main area from sidebar", "h": "Jump to main area from sidebar",
"h": "Jump to sidebar from main area", "l": "Jump to sidebar from main area",
"g": "Jump to first row/top of page", "g": "Jump to first row/top of page",
"G": "Jump to last row/bottom of page", "G": "Jump to last row/bottom of page",
} }
+2
View File
@@ -141,6 +141,8 @@ class RefreshButton(IconTextButton):
def _on_refresh_clicked(self, button: Self) -> None: def _on_refresh_clicked(self, button: Self) -> None:
"""Spawned in a thread""" """Spawned in a thread"""
if self.controller.get_active_treeview().is_loaded() is False:
return
self.is_clicked = True self.is_clicked = True
self.controller.refresh_tree() self.controller.refresh_tree()
# TODO: get server tab enum # TODO: get server tab enum
+8 -4
View File
@@ -80,14 +80,18 @@ class ModSelectionPanel(Gtk.Box):
self.highlight_stale_button.set_sensitive(True) self.highlight_stale_button.set_sensitive(True)
def _on_mods_updated(self, emitter: "Emitter", msg: str, mods: int) -> None: def _on_mods_updated(self, emitter: "Emitter", msg: str, mods: int) -> None:
if mods < 1: self._toggle_panel_sensitivity(mods)
self.main_panel.set_sensitive(False)
self.stale_panel.set_sensitive(False) def _toggle_panel_sensitivity(self, mods: int) -> None:
state = bool(mods)
for el in self.main_panel, self.stale_panel:
el.set_sensitive(state)
def _on_mods_highlighted(self, emitter: "Emitter") -> None: def _on_mods_highlighted(self, emitter: "Emitter") -> None:
self.swap_sensitive(True) self.swap_sensitive(True)
def _on_mod_page_toggled(self, emitter: "Emitter", state: bool) -> None: def _on_mod_page_toggled(self, emitter: "Emitter", state: bool, mods: int) -> None:
self._toggle_panel_sensitivity(mods)
self.set_visible(state) self.set_visible(state)
def swap_sensitive(self, state: bool) -> None: def swap_sensitive(self, state: bool) -> None:
+7 -2
View File
@@ -64,10 +64,15 @@ class Mods(Gtk.Box):
self.controller.open_offline() self.controller.open_offline()
def _on_unmap(self, widget: Self) -> None: def _on_unmap(self, widget: Self) -> None:
self.emitter.emit("mod_page_toggled", False) self.emitter.emit("mod_page_toggled", False, 0)
def _on_map(self, widget: Self) -> None: def _on_map(self, widget: Self) -> None:
self.emitter.emit("mod_page_toggled", True) model = self.tree.get_model()
if model is None:
mods = 0
else:
mods = len(model)
self.emitter.emit("mod_page_toggled", True, mods)
# TODO: delegation # TODO: delegation
# NOTE: handles going back from offline mods page # NOTE: handles going back from offline mods page
# more generic cache restoration method # more generic cache restoration method
+1 -1
View File
@@ -4,7 +4,7 @@ description = "DayZ server browser and mod manager for Linux"
authors = [ authors = [
{name = "aclist"} {name = "aclist"}
] ]
version = "7.0.0b13" version = "7.0.0b14"
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
license-files = ["LICENSE"] license-files = ["LICENSE"]
readme = "README.md" readme = "README.md"
+8
View File
@@ -0,0 +1,8 @@
1559212036,1771519119
1564026768,1770917948
2545327648,1780174455
2276010135,1756744083
1654462998,1780501511
3410710885,1752864732
2918418331,1780413260
3739934289,1780855136
+52
View File
@@ -0,0 +1,52 @@
import pytest
import shutil
import tempfile
from pathlib import Path
from typing import TYPE_CHECKING
import dzgui.api.mods
import dzgui.config.query
from tests.fixtures import fixture_path
if TYPE_CHECKING:
from dzgui.const.enums import Preferences
pytestmark = pytest.mark.mods
@pytest.fixture
def versions() -> str:
return fixture_path("dzg.versions")
@pytest.fixture
def tmp() -> str:
with tempfile.NamedTemporaryFile(delete=False) as f:
tmp = f.name
return tmp
def mock_local_ids(path: Path) -> list[int]:
return [3410710885, 3739934289]
def mock_lookup(path: Path, prefs: "Preferences") -> str:
return ""
def test_signatures(monkeypatch, tmp: str, versions: str) -> None:
ids = [1559212036, 1654462998]
shutil.copyfile(versions, tmp)
path = Path(tmp)
monkeypatch.setattr("dzgui.api.mods.lookup", mock_lookup)
dzgui.api.mods.remove_stale_signatures(path, path, ids)
with open(tmp, "r") as f:
lines = f.readlines()
assert ids not in lines
def test_signatures_with_no_ids(monkeypatch, tmp: str, versions: str) -> None:
shutil.copyfile(versions, tmp)
path = Path(tmp)
monkeypatch.setattr("dzgui.api.mods.get_local_mod_ids", mock_local_ids)
monkeypatch.setattr("dzgui.api.mods.lookup", mock_lookup)
dzgui.api.mods.remove_stale_signatures(path, path)
with open(tmp, "r") as f:
lines = f.readlines()
assert lines == ["3410710885,1752864732\n", "3739934289,1780855136\n"]