Compare commits

...
Author SHA1 Message Date
aclist f76c400697 Merge pull request #535 from aclist/change/enum-dataclasses
Mirror to Codeberg / mirror-to-codeberg (push) Waiting to run
change: enum dataclasses
2026-11-11 03:44:58 +09:00
aclist 7ae5880890 chore: update union
Mirror to Codeberg / mirror-to-codeberg (push) Waiting to run
2026-11-11 03:36:14 +09:00
aclist afbced228d Merge branch 'dzgui7' into change/enum-dataclasses 2026-11-11 03:33:44 +09:00
aclist f5d7bf713d Merge pull request #538 from aclist/fix/clipboard-button-controller
fix: clipboard button controller
2026-11-11 03:32:27 +09:00
aclist c1630adee1 chore: update return value 2026-11-11 03:31:17 +09:00
aclist 88f6b84bea fix: restore dropped typehint 2026-11-11 03:29:23 +09:00
aclist 134dcb6f0b fix: unused param blocking boot 2026-11-11 03:27:12 +09:00
aclist cc0354c881 chore: drop unused imports 2026-11-11 03:21:31 +09:00
aclist f2d21be347 Merge pull request #534 from aclist/chore/strict-typehinting
chore: clear strict typehinting errors
2026-11-11 03:19:29 +09:00
aclist f65fa153cc Merge branch 'chore/strict-typehinting' of github.com:aclist/dzgui into chore/strict-typehinting
Mirror to Codeberg / mirror-to-codeberg (push) Waiting to run
2026-11-11 03:18:50 +09:00
aclist 8aa7328d8f chore: drop unused import 2026-11-11 03:18:40 +09:00
aclist bb04854cd7 chore: add keybinding to strings 2026-11-11 03:17:29 +09:00
aclist 5014b2a467 Merge branch 'dzgui7' into chore/strict-typehinting 2026-11-11 03:15:26 +09:00
aclist d020295aa3 Merge pull request #537 from aclist/fix/cannot-acquire-lock
fix: cannot acquire lock
2026-11-11 03:12:31 +09:00
aclist 1879347831 chore: add typehints 2026-11-11 03:09:39 +09:00
aclist 5a1c098af7 fix: acquire lock prior to calling build_gui() 2026-11-11 03:09:14 +09:00
aclist 3d346dfb07 Merge pull request #531 from aclist/chore/update-strings-pass-1
chore: update strings pass 1
2026-11-11 03:06:43 +09:00
aclist cb03db6032 Merge branch 'dzgui7' into chore/strict-typehinting 2026-11-11 02:47:40 +09:00
aclist c69022f74c Merge pull request #532 from aclist/drop/no-e402
chore: drop unused Gtk imports
2026-11-11 02:44:40 +09:00
aclist 7f0bccad64 Merge pull request #528 from aclist/drop/abstract-constructor-params
drop: abstracted constructor params
2026-11-11 02:43:30 +09:00
aclist d6b5c7bbb4 chore: drop unused imports
Mirror to Codeberg / mirror-to-codeberg (push) Waiting to run
2026-11-11 01:45:00 +09:00
aclist f0a07cbbba Merge branch 'dzgui7' into drop/abstract-constructor-params 2026-11-11 01:41:58 +09:00
aclist 30fa762780 fix: update bool 2026-11-11 01:35:12 +09:00
aclist 807a530550 Merge pull request #529 from aclist/change/normalize-icon-names
change: normalize icon names
2026-11-11 01:31:08 +09:00
aclist f092cfdf4a Merge pull request #530 from aclist/change/normalize-endpoint-names
change: normalize endpoint names
2026-11-11 01:22:59 +09:00
aclist d5c597ab03 Merge pull request #527 from aclist/docs/history-file-2
docs: add history file
2026-11-11 01:15:33 +09:00
aclist 0c4026daf8 chore: clear typehinting errors 2026-11-10 10:02:41 +09:00
aclist e03c6905c6 chore: clear typehinting errors 2026-11-10 06:30:12 +09:00
aclist 8fb3462f35 chore: add placeholder for row struct 2026-11-10 05:58:22 +09:00
aclist bfe9b9d147 chore: clear typehinting errors 2026-11-10 05:58:02 +09:00
aclist 9f36569fd4 drop: LoggerAlertsButton 2026-11-10 05:10:56 +09:00
aclist da18b41136 chore: drop Soup2 stubs 2026-11-10 05:09:02 +09:00
aclist f092207c61 chore: clear strict typehinting errors 2026-11-10 04:37:51 +09:00
aclist c94e4e73db chore: add strings file 2026-11-10 04:34:38 +09:00
aclist 8da264686a chore: clear typehinting errors 2026-11-10 04:33:54 +09:00
aclist 598973104c change: reference dataclass values in enum 2026-11-10 04:30:40 +09:00
aclist 15d4b71350 Merge pull request #533 from aclist/docs/more-attribution
Mirror to Codeberg / mirror-to-codeberg (push) Waiting to run
chore: add remarks to README
2026-11-10 03:08:12 +09:00
aclist 16d3df618a chore: add remarks to README 2026-11-10 03:07:30 +09:00
aclist 4e5fadf928 chore: drop old comments 2026-11-10 03:04:44 +09:00
aclist 3dc37561be chore: update pyproject.toml 2026-11-10 03:02:35 +09:00
aclist 13786591b3 chore: restore exclusion for GLibUnix 2026-11-10 03:02:06 +09:00
aclist 9eb1342877 drop: unused imports script 2026-11-10 03:00:35 +09:00
aclist 3f29484895 chore: drop unused Gtk imports 2026-11-10 02:59:22 +09:00
aclist 22dd8fc67f Merge pull request #525 from aclist/change/get-git-root
change: get git root
2026-11-10 02:44:51 +09:00
aclist fa2f86b03e chore: abstract string literals 2026-11-10 02:41:24 +09:00
aclist c8fe6ec056 chore: update strings files 2026-11-10 02:37:22 +09:00
aclist 6901e5456e drop: unused dataclass 2026-11-10 02:36:18 +09:00
aclist 49298d9943 change: normalize endpoint names 2026-11-09 23:51:19 +09:00
aclist 72e3d95808 change: normalize icon names 2026-11-09 23:48:10 +09:00
aclist a42c9d4389 drop: abstracted constructor params 2026-11-09 23:28:50 +09:00
aclist 03fa707305 chore: remove unused imports 2026-11-09 11:04:56 +09:00
aclist 2479cbd41b change: parse repository root 2026-11-09 11:03:50 +09:00
98 changed files with 713 additions and 750 deletions

No files matched your search

+2
View File
@@ -37,6 +37,8 @@ Users wishing to review the licenses to these components can inspect the `LICENS
Steam and the Steam logo are trademarks and/or registered trademarks of Valve Corporation in the U.S. and/or other countries.
DayZ is a trademark and brand of Bohemia Interactive a.s.
## Disclaimer
DZGUI is beta-quality software and is provided as-is.
+2 -1
View File
@@ -111,7 +111,8 @@ 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, Any], lines: list[Any] = []) -> str:
self.indent: int
t1 = "AppWorkshop"
t2 = ("WorkshopItemsInstalled", "WorkshopItemDetails")
for k, v in d.items():
+1 -13
View File
@@ -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)
@@ -114,10 +106,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 +140,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 []
+6 -4
View File
@@ -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]
+6 -6
View File
@@ -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:
-6
View File
@@ -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()
@@ -158,9 +155,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:
+2 -1
View File
@@ -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)
+2 -2
View File
@@ -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"]
-29
View File
@@ -37,37 +37,8 @@ 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
NO_PADDING = 0
NO_EXPAND = False
NO_FILL = False
EXPAND = True
FILL = True
SCROLL_INCREMENT = 50
WINDOW_DEFAULT_X = 1400
WINDOW_DEFAULT_Y = 800
+3 -3
View File
@@ -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"
+224 -206
View File
@@ -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 | None
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) -> NotebookPage | None:
return self.value.opens
HELP_MENU_ROWS = (
+20
View File
@@ -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"
+2 -2
View File
@@ -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
+12 -11
View File
@@ -8,7 +8,7 @@ import dzgui.util._json as JSON # noqa
from dzgui.const.enum import (
ButtonType,
ContextMenu,
ContextMenuItem,
FilterMode,
NotebookPage,
Preferences,
@@ -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)
@@ -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
@@ -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:
@@ -262,7 +264,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 +276,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 +310,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)
@@ -355,22 +357,21 @@ 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:
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()
+3
View File
@@ -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:
+2 -2
View File
@@ -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:
@@ -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()
+7 -14
View File
@@ -48,18 +48,13 @@ 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
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
@@ -197,9 +192,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 +271,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
@@ -298,22 +291,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}"
+19 -19
View File
@@ -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:
+6 -13
View File
@@ -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
@@ -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,11 +74,11 @@ 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)
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:
@@ -106,17 +105,11 @@ 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:
def get_all_filters(self) -> tuple[str, ...]:
map_name = self.get_active_map_name()
enabled = self.get_filters()
kw = self.get_active_keyword()
+2 -2
View File
@@ -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
@@ -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:
+1 -1
View File
@@ -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)
+4 -4
View File
@@ -15,15 +15,15 @@ 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)
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)
+2 -7
View File
@@ -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
@@ -60,9 +55,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]
+4 -4
View File
@@ -7,11 +7,11 @@ 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
GObject.type_from_name(GObject.type_name(ptype)): ptype # type: ignore[no-untyped-call]
for ptype in (int, float, str, bool, object)
}
@@ -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
+22 -8
View File
@@ -1,10 +1,12 @@
import re
from dataclasses import dataclass
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
@@ -16,6 +18,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
@@ -30,7 +48,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]],
] = {}
@@ -51,9 +69,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:
@@ -67,7 +82,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],
@@ -175,7 +189,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]
@@ -247,7 +261,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))
@@ -258,7 +272,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:
+4 -4
View File
@@ -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
@@ -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))
+14
View File
@@ -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"
+2
View File
@@ -29,3 +29,5 @@ toggle_calc = "Toggle calculator"
server_details = "Server details"
server_message = "Server message"
caught_cancel_signal = "Caught cancel signal, cleaning up"
+4
View File
@@ -0,0 +1,4 @@
all_maps = "All maps"
filters_heading = "Filters"
keyword_placeholder = "Filter by keyword"
map_placeholder = "Filter by map"
+1
View File
@@ -1 +1,2 @@
tab_changed_label = "*"
selectable_tree = "Ctrl-click to select multiple; Shift-click to select a range."
+5
View File
@@ -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}'"
+2
View File
@@ -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"
+29
View File
@@ -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"
+2 -1
View File
@@ -3,4 +3,5 @@ mod_id = "ID"
up_to_date = "Up to date"
modlist = "Modlist"
checkmark = "✓"
resync="⟳"
resync = "⟳"
invalid_mod = "Invalid mod"
+1 -2
View File
@@ -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:
+1 -1
View File
@@ -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:
+2 -7
View File
@@ -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
@@ -59,7 +54,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 +67,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()
+6 -2
View File
@@ -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:
+3 -2
View File
@@ -1,4 +1,5 @@
import re
from typing import Any
from dzgui.util.localize import number
from dzgui.strings.generic import selectable_tree
@@ -7,7 +8,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:
@@ -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
+1 -1
View File
@@ -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(".")]
+4 -1
View File
@@ -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
+3 -1
View File
@@ -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:
"""
+1 -1
View File
@@ -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
+3 -3
View File
@@ -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",
@@ -159,9 +160,6 @@ filter_unofficial = "Unofficial"
filter_modded = "Modded"
filter_unmodded = "Unmodded"
# maps
all_maps = "All maps"
# state
unknown = "Unknown"
none_provided = "None provided"
@@ -252,6 +250,7 @@ class Crumbs:
developers: str
default: str
offline: str
connect: str
@dataclass(slots=True, frozen=True)
@@ -439,6 +438,7 @@ crumbs = Crumbs(
developers="Options > Developers",
default="Servers > ",
offline="Mods > Play offline",
connect="Connect",
)
no_mods = "No local mods found."
+1 -1
View File
@@ -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 # type: ignore
if TYPE_CHECKING:
from dzgui.config.userprefs import UserPrefs
+11 -7
View File
@@ -1,13 +1,13 @@
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
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)
@@ -19,7 +19,11 @@ 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,
@@ -51,9 +55,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,
)
@@ -63,7 +67,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()
+24 -55
View File
@@ -1,28 +1,25 @@
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 (
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_WEB_BROWSER,
)
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
@@ -64,7 +61,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:
@@ -101,15 +98,13 @@ class LargeIconTextButton(IconButton):
class ClipboardButton(IconTextButton):
def __init__(self, controller: Union["Controller", None], func: Callable) -> None:
super().__init__(CLIPBOARD, atomic_buttons.copy)
self.controller = controller
def __init__(self, func: Callable[[], str]) -> None:
super().__init__(ICON_CLIPBOARD, atomic_buttons.copy)
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 _on_button_clicked(self, button: Self, func: Callable[[], str]) -> None:
def revert() -> Literal[False]:
self.revert_icon()
return False
@@ -120,33 +115,30 @@ 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")
self.set_tooltip_text(buttons.clipboard_ip_button)
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")
self.set_tooltip_text(buttons.unset_button_tooltip)
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 +203,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 +220,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,33 +240,10 @@ 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)
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:
super().__init__()
+16 -15
View File
@@ -1,8 +1,14 @@
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.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,
@@ -18,7 +24,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
@@ -30,27 +36,24 @@ 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
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.set_image(Icon(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.set_image(Icon(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 +84,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)
@@ -187,10 +189,9 @@ 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")
self.unset_button = CloseButton(unset_button_label)
self.unset_button.connect("clicked", self._on_unset_clicked)
if favorite is None:
+2 -2
View File
@@ -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
@@ -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:
+12 -14
View File
@@ -1,15 +1,16 @@
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.options import api_placeholder, api_validate
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
@@ -38,7 +39,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 +47,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 +72,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,21 +141,19 @@ 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(
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_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)
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)
@@ -206,9 +204,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)
@@ -222,7 +220,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:
+3 -3
View File
@@ -1,12 +1,12 @@
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
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
@@ -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()
+21 -19
View File
@@ -3,21 +3,22 @@ from typing import Literal, TYPE_CHECKING
from dzgui.const.constants import (
APP_NAME,
EXPAND,
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.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
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)
@@ -30,7 +31,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__(
@@ -42,7 +43,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():
@@ -93,14 +97,13 @@ 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
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
@@ -109,7 +112,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)
@@ -174,8 +177,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,13 +197,12 @@ 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)
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 +212,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()
@@ -305,6 +306,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
+1 -1
View File
@@ -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):
+1 -1
View File
@@ -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):
+1 -1
View File
@@ -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):
+3 -2
View File
@@ -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()
+6 -7
View File
@@ -1,14 +1,13 @@
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
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
@@ -20,8 +19,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):
@@ -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)
+8 -13
View File
@@ -6,13 +6,10 @@ 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
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
@@ -23,7 +20,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
@@ -65,20 +62,19 @@ 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)
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=False, padding=0)
self.update_button = IconTextButton(
"dialog-information-symbolic", label="Updates available"
"dialog-information-symbolic", label=update_button
)
self.gutter_box = Gtk.Box(
@@ -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
@@ -131,7 +127,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)
+3 -7
View File
@@ -2,12 +2,11 @@ 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
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
@@ -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)
@@ -60,7 +56,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 +148,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)
+2 -1
View File
@@ -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):
+3 -4
View File
@@ -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
@@ -105,7 +104,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
)
@@ -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()
+4 -4
View File
@@ -3,14 +3,14 @@ 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
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)
@@ -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:
+2 -2
View File
@@ -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
@@ -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:
+8 -8
View File
@@ -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):
@@ -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
+9 -11
View File
@@ -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
@@ -8,7 +7,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
@@ -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)
@@ -140,8 +139,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:
@@ -187,7 +185,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)
@@ -272,7 +270,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 +294,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)
+2 -3
View File
@@ -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
@@ -9,7 +8,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:
@@ -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)
+11 -19
View File
@@ -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()
+2 -2
View File
@@ -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)
+5 -5
View File
@@ -18,11 +18,11 @@ 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):
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():
+12 -7
View File
@@ -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
@@ -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,
@@ -421,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,
@@ -471,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
@@ -776,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)
@@ -787,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)
+11 -13
View File
@@ -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
@@ -9,15 +9,13 @@ 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)
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:
@@ -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:
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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)
+2 -2
View File
@@ -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
+1 -1
View File
@@ -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
+5 -4
View File
@@ -7,12 +7,13 @@ 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
# 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,
+1 -1
View File
@@ -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
+4 -6
View File
@@ -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])
+13 -16
View File
@@ -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
@@ -13,12 +13,8 @@ from dzgui.const.constants import (
FLATPAK_RUN_CMD,
FLATPAK_SANDBOX,
FLATPAK_STEAM_PATH,
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
@@ -55,7 +51,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
@@ -67,7 +63,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 +127,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)
@@ -143,7 +139,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 +206,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:
@@ -234,8 +230,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
@@ -406,7 +402,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,
@@ -439,7 +436,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)
+7 -7
View File
@@ -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
@@ -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:
@@ -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)
@@ -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()
+3 -3
View File
@@ -5,12 +5,13 @@ 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
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)
@@ -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()
+1 -1
View File
@@ -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:
+9 -25
View File
@@ -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:
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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)
+6 -5
View File
@@ -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
@@ -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] = []
@@ -250,9 +251,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:
@@ -406,7 +407,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]:
+2 -1
View File
@@ -78,6 +78,7 @@ line-length = 88
indent-width = 4
extend-exclude = ["*lib/a2s"]
lint.unfixable = ["F401", "I001"]
lint.ignore = ["E402"]
[tool.ruff.format]
quote-style = "double"
@@ -104,7 +105,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 = [
+2 -2
View File
@@ -1,4 +1,4 @@
from pathlib import Path
from shared import get_git_root
BOILERPLATE = """
This file includes the combined licenses to the following components:
@@ -40,7 +40,7 @@ class CombinedLicense:
def concat_license() -> str:
combined = CombinedLicense()
root = Path(__file__).resolve().parents[1]
root = get_git_root()
licenses = root.joinpath("docs/licenses")
main_license = root.joinpath(LICENSE_FILE)
+5 -4
View File
@@ -1,10 +1,11 @@
import shutil
import subprocess
from pathlib import Path
par = Path(__file__).parent.parent
clog = par / "CHANGELOG.md"
target = par / "dzgui/data/CHANGELOG.md"
from shared import get_git_root
root = get_git_root()
clog = root.joinpath("CHANGELOG.md")
target = root.joinpath("dzgui/data/CHANGELOG.md")
shutil.copy(clog, target)
+2 -3
View File
@@ -1,10 +1,9 @@
import tomllib
from pathlib import Path
root = Path(__file__).resolve().parents[1]
from shared import get_git_root
def get_entrypoint() -> None:
root = get_git_root()
p = root.joinpath("pyproject.toml")
with open(p, "rb") as f:
t = tomllib.load(f)
+3 -2
View File
@@ -3,14 +3,15 @@ import requests
import subprocess
import tarfile
from pathlib import Path
from urllib.parse import unquote
from shared import get_git_root
BUILD = "3.13.9"
DATE = "20251014"
CPYTHON_BUILD = f"cpython-{BUILD}"
root = Path(__file__).resolve().parents[1]
root = get_git_root()
build_dir = root.joinpath("build")
cpython_dir = build_dir.joinpath(CPYTHON_BUILD)
+2 -1
View File
@@ -9,6 +9,7 @@ from pathlib import Path
from concat_licenses import concat_license
from parse_toml import get_entrypoint
from prebuild import get_pyapp, rebuild_cpython
from shared import get_git_root
def update_uname(info: tarfile.TarInfo) -> tarfile.TarInfo:
@@ -17,7 +18,7 @@ def update_uname(info: tarfile.TarInfo) -> tarfile.TarInfo:
return info
root = Path(__file__).resolve().parents[1]
root = get_git_root()
dist_dir = root.joinpath("dist")
build_dir = root.joinpath("build")
+1 -10
View File
@@ -1,16 +1,7 @@
import subprocess
from pathlib import Path
from dzgui.app_init import build_gui
def get_git_root() -> Path:
proc = subprocess.run(
["git", "rev-parse", "--show-toplevel"], capture_output=True, text=True
)
if proc.returncode != 0:
raise OSError("Failed to parse git root")
return Path(proc.stdout.rstrip())
from shared import get_git_root
def get_image_path() -> Path:
+11
View File
@@ -0,0 +1,11 @@
import subprocess
from pathlib import Path
def get_git_root() -> Path:
proc = subprocess.run(
["git", "rev-parse", "--show-toplevel"], capture_output=True, text=True
)
if proc.returncode != 0:
raise OSError("Failed to parse git root")
return Path(proc.stdout.rstrip())
-38
View File
@@ -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)
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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