Compare commits

..
Author SHA1 Message Date
aclist 4e5fadf928 chore: drop old comments 2026-11-10 03:04:44 +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
46 changed files with 229 additions and 337 deletions

No files matched your search

-124
View File
@@ -1,124 +0,0 @@
# History of the project
This file collects notable milestones and events related to DZGUI, mainly around DZGUI as a framework and the history of development.
A chronological summary of features can be found in CHANGELOG.md.
## 2021
### 2021-12-03: DayZ adds support for Battleye over Proton
DayZ was among a handful of games with whitelisted anti-cheat support under Valve's emerging WINE-based Proton compatibility layer.
While adding anti-cheat compatibility at a technical level, this change did not actually allow the client to connect to servers running complex modlists.
Development of the precursor to DZGUI began at around this time.
### 2021-12-12: initial version of DZTUI is created
DZTUI, a Bash script and terminal user interface, was the precursor to DZGUI.
This was an initial attempt at bootstrapping the Steam client to handle mods correctly when launching DayZ.
Due to user interest, features continued to be added. This early tool is archived under the 'dztui' branch.
## 2022
### 2022-05-09: addition of zenity GUI and project name change (DZGUI)
With the introduction of the Steam Deck, more users wanted a graphical user interface
to interact with DZTUI. Being that DZTUI was purely a Bash script, a method of
drawing menus was needed, so the "zenity" package was used to create prefabricated dialogs.
While this met the minimum requirement to show prompts and selectable options, no rich customization was possible.
The introduction of zenity as a dependency marked the shift from "DZTUI" to "DZGUI".
Throughout 2022, the limits of zenity for DZGUI's purposes were tested, particularly in the context of SteamOS.
This led to the discovery of a number of bugs in zenity, GTK, and SteamOS, which were reported up to their respective channels.
While this was improving the SteamOS user experience, it started to become clear that a fully custom UI was needed.
This barebones version of DZGUI sufficed for some time, with the Bash script and some standalone Python modules doing the heavy lifting,
and this period served to surface a number of bugs and interesting interactions with the Steam client.
## 2023
### 2023-11-23: Python 3.10 dependency
Prior to this time, DZGUI open-endedly allowed you to use any Python version for its modules, without any significant handling for
the idiosyncrasies of different versions. A minimum requirement of Python 3.10 was thus instituted to normalize expected behavior.
Throughout 2023, various features related to server browsing were added, such as filters and more granular search parameters.
There were still a lot of unknowns at this time, so things were being resolved through trial and error.
## 2024
### 2024-01-31: DZGUI 5 API introduces GTK UI
Using zenity as a simple GUI had exposed some bugs and limitations in that package, particularly when trying to push huge lists of server records
into tables and sorting them in a performant manner.
Around this time it became clear that a more robust UI framework was needed. While keeping the original Bash script and Python modules for
background processing, PyGObject (GTK) was added to the project in order to ship a custom UI that could be designed from the ground up.
However, it should be noted that at this point, the DZGUI logic itself was still a (increasingly large) Bash script to which a GTK UI had been stitched on.
These were totally separate components, with UI actions on the GTK side triggering calls to Bash functions in an overly complex helper file.
The UI was basically a thin client around the old Bash logic, but this was still a far cry from an integrated application with proper signals and dedicated components.
Effectively, DZGUI 5 through 6 were a stepping stone that set the groundwork to go from DZGUI 4 (pure Bash script) to
DZGUI 7 (standalone Python application). Today, little of DZGUI 5's original logic remains outside of the basic UI grid.
### 2024-10-15: Frostline DLC released, widespread parsing problems
DayZ's first DLC introduced a new format for server responses that packed extra metadata into the A2S_RULES format.
Upon its release, all third-party DayZ launchers and server listing web sites were unable to parse the mod information being returned from Frostline servers.
For a period of two weeks, this remained unsolved and various attempts were made to understand the updated specification.
### 2024-10-30: DZGUI hotfix adds support for Frostline servers
DZGUI was the first third-party tool to fix parsing for Frostline servers and restore functionality.
This was a small milestone, because it meant DZGUI was evolving from a "dumb" launcher to a featureful tool
touching upon and improving all aspects of the end-to-end DayZ experience.
Particularly around this time, the sheer diversity of needs of modders, server operators, and players started to become clear.
Users had highly specific and diverse use cases for setting up their game environment, and requested a number of features to support that on
the DZGUI side.
## 2025
### 2025-01-17: DZGUI 6 API
With DZGUI 6, the difficult decision to enforce a Python 3.13 dependency was made.
While the previous dependency was Python 3.10+, it was deemed better to develop against and target identical versions.
However, due to the vagaries of managing Python environments on end-user systems, this change added unnecessary complexity and frustration.
Users had to properly set up virtual environments, install the relevant dependencies, and create a wrapper script to run this version, whereas
previous versions had essentially been point and click.
### 2025-10-23: DZGUI 7 API and the "Ship of Theseus"
It had become clear that the situation above was untenable and the legacy Bash script was unmaintainable given the project's enlarged scope.
The lack of a robust testing or typehinting framework made it difficult to diagnose rare bugs.
Shipping a standalone, turnkey application with zero or minimal dependencies was the most urgent priority.
DZGUI was still carrying around small bits and pieces of the old Bash script logic, so the decision was made to
refactor the application into a pure Python + GTK tool. This would effectively be a total rewrite of all logic,
only preserving the most minimal skeleton of the original UI layout as a reference.
Over the course of a year, a wholesale "Ship of Theseus" rewrite took place.
Nearly every component of DZGUI 6, both API and UI side, was taken apart and replaced.
A soft alpha and beta phase were used to iterate on this concept further. DZGUI 7 was increasingly recommended to users
as the preferred choice. This allowed for keeping the established userbase on the (relatively) stable DZGUI 6 spec, while building up
the new spec and making it available to more users as features were elaborated.
Through late 2025, several LLM-generated applications that recycled significant parts of DZGUI's code without the applicable GPL license were found.
The maintainers of those projects were advised to excise the offending segments or apply the necessary GPL licenses.
## 2026
### 2026-05-17: Switch to standalone releases
The DZGUI 7 beta starts shipping standalone glibc builds with an integrated setup wizard and installer.
DZGUI 6 had relied on "over the air" updates delivered via either a "stable" or "testing" branch
(archived respectively under the "dzgui" and "testing" branches). Effectively, the script would bootstrap itself and
fetch raw files directly from the repository. This was prone to problems because updates were monolithic, not giving
an easy escape hatch if the user needed to rollback in the event of a regression.
This was changed to atomic, tagged releases. In addition to updating directly through DZGUI, these prebuilt releases can be
downloaded manually as needed, and rollbacks are available for all prior versions under the DZGUI 7 specification.
### 2026-10-27: 500 tickets closed
The milestone of 500 tickets/PRs being closed was reached.
2026 was by far DZGUI's most intensive year by commits, perhaps suggesting the scope of the rewrite:
- 2021: 16
- 2022: 434
- 2023: 322
- 2024: 347
- 2025: 426
- 2026: 1300
DZGUI 7 is scheduled to release as a stable version within the year.
-2
View File
@@ -37,8 +37,6 @@ 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. 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 ## Disclaimer
DZGUI is beta-quality software and is provided as-is. DZGUI is beta-quality software and is provided as-is.
-8
View File
@@ -24,14 +24,6 @@ from typing import Any
logger = logging.getLogger(APP_NAME) 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]: def get_local_mod_ids(steam_path: Path) -> list[int]:
workshop_path = get_local_mod_path(steam_path) workshop_path = get_local_mod_path(steam_path)
mods = get_local_mods(workshop_path) mods = get_local_mods(workshop_path)
+6 -6
View File
@@ -31,10 +31,10 @@ from dzgui.const.constants import (
VDF_PATH, VDF_PATH,
) )
from dzgui.const.endpoints import ( from dzgui.const.endpoints import (
STEAM_APP_DETAILS, APP_DETAILS,
SUB_ENDPOINT,
STEAM_PUBLISHED_FILES, STEAM_PUBLISHED_FILES,
STEAM_SUB, UNSUB_ENDPOINT,
STEAM_UNSUB,
) )
from dzgui.strings import wizard from dzgui.strings import wizard
from dzgui.strings.errors import api_validation_error, workshop_endpoint_error 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: def subscribe(key: str, mod: int) -> None:
update_workshop(key, mod, STEAM_SUB) update_workshop(key, mod, SUB_ENDPOINT)
def unsubscribe(key: str, mod: int) -> None: def unsubscribe(key: str, mod: int) -> None:
update_workshop(key, mod, STEAM_UNSUB) update_workshop(key, mod, UNSUB_ENDPOINT)
def validate_mod(client: str, mod: str) -> None: 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: def get_app_name(appid: int) -> str | None:
payload = {"appids": [appid]} payload = {"appids": [appid]}
res = requests.get(STEAM_APP_DETAILS, params=payload) res = requests.get(APP_DETAILS, params=payload)
if res.status_code != 200: if res.status_code != 200:
return None return None
try: try:
-3
View File
@@ -158,9 +158,6 @@ def build_gui(version: str, is_debug: bool) -> App | None:
f.truncate(0) f.truncate(0)
if _is_steam_deck is False: 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): if FLATPAK_ROOT_PATH in lookup(XDG.config, Preferences.DEFAULT):
has_client = has_flatpak_client() has_client = has_flatpak_client()
else: else:
+29
View File
@@ -37,8 +37,37 @@ HEX_GREEN = "#32CD32"
HEX_RED = "#FF0000" HEX_RED = "#FF0000"
HEX_ORANGE = "#FFAC1C" 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" SEPARATOR = "SEPARATOR"
# TODO: drop
NO_PADDING = 0
NO_EXPAND = False
NO_FILL = False
EXPAND = True
FILL = True
SCROLL_INCREMENT = 50 SCROLL_INCREMENT = 50
WINDOW_DEFAULT_X = 1400 WINDOW_DEFAULT_X = 1400
WINDOW_DEFAULT_Y = 800 WINDOW_DEFAULT_Y = 800
+3 -3
View File
@@ -3,9 +3,9 @@ STEAM_PUBLISHED_FILES = (
"https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1" "https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1"
) )
STEAM_SERVERS = "https://api.steampowered.com/IGameServersService/GetServerList/v1" STEAM_SERVERS = "https://api.steampowered.com/IGameServersService/GetServerList/v1"
STEAM_SUB = "https://api.steampowered.com/IPublishedFileService/Subscribe/v1" SUB_ENDPOINT = "https://api.steampowered.com/IPublishedFileService/Subscribe/v1"
STEAM_UNSUB = "https://api.steampowered.com/IPublishedFileService/Unsubscribe/v1" UNSUB_ENDPOINT = "https://api.steampowered.com/IPublishedFileService/Unsubscribe/v1"
STEAM_APP_DETAILS = "https://store.steampowered.com/api/appdetails?" APP_DETAILS = "https://store.steampowered.com/api/appdetails?"
GITHUB = "https://github.com/aclist" GITHUB = "https://github.com/aclist"
GITHUB_RELEASES = "https://api.github.com/repos/aclist/dztui/releases/latest" GITHUB_RELEASES = "https://api.github.com/repos/aclist/dztui/releases/latest"
-20
View File
@@ -1,20 +0,0 @@
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"
+3 -5
View File
@@ -48,7 +48,7 @@ from dzgui.strings.dialogs import (
waiting_for_directories, waiting_for_directories,
) )
from dzgui.strings import kb 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.format import format_exception, format_mib
from dzgui.util.strings import dialog, server_timeout from dzgui.util.strings import dialog, server_timeout
from dzgui.util.symlink import rebuild_symlinks from dzgui.util.symlink import rebuild_symlinks
@@ -197,9 +197,8 @@ class ConnectionManager:
is_last = self.is_last_server() is_last = self.is_last_server()
# TODO: strings
invalid_mods = [ 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( prereqs = Prerequisites(
@@ -277,8 +276,7 @@ class ConnectionManager:
for mod in alpha_mods: for mod in alpha_mods:
# NOTE: if the mod is neither synched or out of date, it is a malformed mod # NOTE: if the mod is neither synched or out of date, it is a malformed mod
if mod[2] == "": if mod[2] == "":
# TODO: strings mod[2] = invalid_mod
mod[2] = "Invalid mod"
return alpha_mods, missing_mods return alpha_mods, missing_mods
+3 -9
View File
@@ -2,7 +2,7 @@ from typing import TYPE_CHECKING
from dzgui.model.model_factory import ModelFactory from dzgui.model.model_factory import ModelFactory
from dzgui.util import strings from dzgui.util import strings
from dzgui.util.strings import all_maps from dzgui.strings.filters import all_maps
if TYPE_CHECKING: if TYPE_CHECKING:
from dzgui.model.model_factory import FastInsertListStore from dzgui.model.model_factory import FastInsertListStore
@@ -106,16 +106,10 @@ class FilterManager:
self.reinit_map_store() self.reinit_map_store()
maps.sort() maps.sort()
for m in maps: clean = [m for m in maps if m != all_maps]
# TODO: strings for m in clean:
if m == "All maps":
continue
self.append_map([m]) 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:
map_name = self.get_active_map_name() map_name = self.get_active_map_name()
enabled = self.get_filters() enabled = self.get_filters()
+2 -1
View File
@@ -5,6 +5,7 @@ from typing import Any, TYPE_CHECKING, Union
from dzgui.const.enum import FilterMode from dzgui.const.enum import FilterMode
from dzgui.model.model_factory import ModelFactory from dzgui.model.model_factory import ModelFactory
from dzgui.util import strings from dzgui.util import strings
from dzgui.strings import filters as str_filters
if TYPE_CHECKING: if TYPE_CHECKING:
from dzgui.managers.filter import FilterManager from dzgui.managers.filter import FilterManager
@@ -175,7 +176,7 @@ class ProxyModelManager:
rows = self.filtered rows = self.filtered
sel_map = self.filter_man.get_active_map_name() sel_map = self.filter_man.get_active_map_name()
if sel_map == strings.all_maps: if sel_map == str_filters.all_maps:
return rows return rows
rows = [row for row in rows if row[1] == sel_map] rows = [row for row in rows if row[1] == sel_map]
+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 = "Steam Workshop"
workshop_tooltip = "Open your Workshop subscriptions" 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_details = "Server details"
server_message = "Server message" 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." 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." fullscreen_eventbox = "This option is not available on Steam Deck."
save_button = "Save" save_button = "Save"
api_placeholder = "Enter API key"
api_failed = "API validation failed" api_failed = "API validation failed"
api_validate = "Validate"
flatpak_label = "Flatpak client" flatpak_label = "Flatpak client"
flatpak_standard = "Standard" flatpak_standard = "Standard"
+2 -1
View File
@@ -3,4 +3,5 @@ mod_id = "ID"
up_to_date = "Up to date" up_to_date = "Up to date"
modlist = "Modlist" modlist = "Modlist"
checkmark = "✓" checkmark = "✓"
resync="⟳" resync = "⟳"
invalid_mod = "Invalid mod"
-3
View File
@@ -159,9 +159,6 @@ filter_unofficial = "Unofficial"
filter_modded = "Modded" filter_modded = "Modded"
filter_unmodded = "Unmodded" filter_unmodded = "Unmodded"
# maps
all_maps = "All maps"
# state # state
unknown = "Unknown" unknown = "Unknown"
none_provided = "None provided" none_provided = "None provided"
+2 -2
View File
@@ -1,7 +1,7 @@
import logging import logging
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from dzgui.const.constants import APP_NAME from dzgui.const.constants import APP_NAME, NO_EXPAND, NO_FILL, NO_PADDING
from dzgui.const.enum import ButtonType from dzgui.const.enum import ButtonType
import gi import gi
@@ -63,7 +63,7 @@ class ButtonBox(Gtk.Box):
self.buttons.append(button) self.buttons.append(button)
button.connect("clicked", self._on_selection_button_clicked) button.connect("clicked", self._on_selection_button_clicked)
self.pack_start(button, expand=False, fill=False, padding=0) self.pack_start(button, NO_EXPAND, NO_FILL, NO_PADDING)
def _focus_first_button(self, emitter: "Emitter") -> None: def _focus_first_button(self, emitter: "Emitter") -> None:
self.buttons[0].grab_focus() self.buttons[0].grab_focus()
+21 -23
View File
@@ -7,15 +7,15 @@ from dzgui.util.strings import (
alert_button_tooltip, alert_button_tooltip,
atomic_buttons, atomic_buttons,
) )
from dzgui.const.icons import ( from dzgui.const.constants import (
ICON_CLIPBOARD, CLIPBOARD,
ICON_CLOSE, CLOSE,
ICON_INPUT_KEYBOARD, INPUT_KEYBOARD,
ICON_LIST_ADD, LIST_ADD,
ICON_REFRESH, REFRESH_ICON,
ICON_STEAM, STEAM_ICON,
ICON_WARNING, WARNING,
ICON_WEB_BROWSER, WEB_BROWSER,
) )
from dzgui.strings import connect_panel from dzgui.strings import connect_panel
@@ -102,12 +102,11 @@ class LargeIconTextButton(IconButton):
class ClipboardButton(IconTextButton): class ClipboardButton(IconTextButton):
def __init__(self, controller: Union["Controller", None], func: Callable) -> None: def __init__(self, controller: Union["Controller", None], func: Callable) -> None:
super().__init__(ICON_CLIPBOARD, atomic_buttons.copy) super().__init__(CLIPBOARD, atomic_buttons.copy)
self.controller = controller self.controller = controller
self.connect("clicked", self._on_button_clicked, func) self.connect("clicked", self._on_button_clicked, func)
# TODO: strings self.set_tooltip_text(buttons.clipboard_button)
self.set_tooltip_text("Copy to clipboard")
def _on_button_clicked(self, button: Self, func: Callable) -> None: def _on_button_clicked(self, button: Self, func: Callable) -> None:
def revert() -> Literal[False]: def revert() -> Literal[False]:
@@ -125,28 +124,26 @@ class CopyIpButton(ClipboardButton):
def __init__(self, controller: Union["Controller", None], func: Callable) -> None: def __init__(self, controller: Union["Controller", None], func: Callable) -> None:
super().__init__(controller, func) super().__init__(controller, func)
# TODO: strings self.set_tooltip_text(buttons.clipboard_ip_button)
self.set_tooltip_text("Copy IP to clipboard")
class CloseButton(IconTextButton): class CloseButton(IconTextButton):
def __init__(self, label: str) -> None: def __init__(self, label: str) -> None:
super().__init__(icon=ICON_CLOSE, label=label) super().__init__(icon=CLOSE, label=label)
# TODO: strings self.set_tooltip_text(buttons.unset_button_tooltip)
self.set_tooltip_text("Unset this server as favorite")
class WebButton(IconTextButton): class WebButton(IconTextButton):
def __init__(self, label: str) -> None: def __init__(self, label: str) -> None:
super().__init__(icon=ICON_WEB_BROWSER, label=label) super().__init__(icon=WEB_BROWSER, label=label)
pass pass
class RefreshButton(IconTextButton): class RefreshButton(IconTextButton):
def __init__(self, controller: "Controller") -> None: def __init__(self, controller: "Controller") -> None:
super().__init__( super().__init__(
icon=ICON_REFRESH, icon=REFRESH_ICON,
label=atomic_buttons.refresh, label=atomic_buttons.refresh,
) )
@@ -211,7 +208,7 @@ class RefreshButton(IconTextButton):
class KeysButton(IconTextButton): class KeysButton(IconTextButton):
def __init__(self, controller: "Controller") -> None: def __init__(self, controller: "Controller") -> None:
super().__init__(icon=ICON_INPUT_KEYBOARD, label=atomic_buttons.keys) super().__init__(icon=INPUT_KEYBOARD, label=atomic_buttons.keys)
self.controller = controller self.controller = controller
self.set_margin_top(10) self.set_margin_top(10)
@@ -228,7 +225,7 @@ class KeysButton(IconTextButton):
class SteamConnectButton(LargeIconTextButton): class SteamConnectButton(LargeIconTextButton):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__(icon=ICON_STEAM, label=connect_panel.connect_button) super().__init__(icon=STEAM_ICON, label=connect_panel.connect_button)
self.set_tooltip_text(connect_panel.connect_tooltip) self.set_tooltip_text(connect_panel.connect_tooltip)
@@ -248,10 +245,11 @@ class SteamWorkshopButton(SteamTextButton):
class AddButton(IconTextButton): class AddButton(IconTextButton):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__(icon=ICON_LIST_ADD, label=connect_panel.add_button) super().__init__(icon=LIST_ADD, label=connect_panel.add_button)
self.set_tooltip_text(connect_panel.add_tooltip) self.set_tooltip_text(connect_panel.add_tooltip)
# TODO: drop
class LoggerAlertsButton(IconTextButton): class LoggerAlertsButton(IconTextButton):
def __init__(self, warnings: int, errors: int) -> None: def __init__(self, warnings: int, errors: int) -> None:
warnings_text = "" warnings_text = ""
@@ -267,7 +265,7 @@ class LoggerAlertsButton(IconTextButton):
separator = "," separator = ","
concat = warnings_text + separator + errors_text concat = warnings_text + separator + errors_text
super().__init__( super().__init__(
icon=ICON_WARNING, icon=WARNING,
label=f"Loaded with{concat}", label=f"Loaded with{concat}",
) )
self.set_halign(Gtk.Align.END) self.set_halign(Gtk.Align.END)
+13 -14
View File
@@ -1,9 +1,13 @@
from typing import Self, TYPE_CHECKING from typing import Self, TYPE_CHECKING
from dzgui.const.icons import ICON_CARET_DOWN, ICON_CARET_UP from dzgui.const.constants import CARET_DOWN, CARET_UP, UDP_PORT
from dzgui.const.constants import UDP_PORT
from dzgui.model.servers import ServerModelManager from dzgui.model.servers import ServerModelManager
from dzgui.strings import connect_panel 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.util.keys import is_ctrl_mask
from dzgui.views.components.buttons import ( from dzgui.views.components.buttons import (
AddButton, AddButton,
@@ -31,27 +35,24 @@ ROWS = 1
class DockButton(IconButton): class DockButton(IconButton):
def __init__(self, child: Gtk.Widget) -> None: def __init__(self, child: Gtk.Widget) -> None:
super().__init__(ICON_CARET_DOWN) super().__init__(CARET_DOWN)
self.expanded = True self.expanded = True
self.child = child self.child = child
self.connect("clicked", self._on_click) 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_margin_top(10)
self.set_tooltip_text(self.collapse_text) self.set_tooltip_text(dock_button_collapse)
def collapse(self) -> None: def collapse(self) -> None:
self.set_image(Icon(ICON_CARET_UP)) self.set_image(Icon(CARET_UP))
self.child.hide() self.child.hide()
self.set_tooltip_text(self.expand_text) self.set_tooltip_text(dock_button_expand)
def expand(self) -> None: def expand(self) -> None:
self.set_image(Icon(ICON_CARET_DOWN)) self.set_image(Icon(CARET_DOWN))
self.child.show() self.child.show()
self.set_tooltip_text(self.collapse_text) self.set_tooltip_text(dock_button_collapse)
def _on_click(self, button: Self) -> None: def _on_click(self, button: Self) -> None:
if self.expanded: if self.expanded:
@@ -82,7 +83,6 @@ class LanPanel(Gtk.Frame):
self.scan = Gtk.Button(label=connect_panel.lan_scan_button) self.scan = Gtk.Button(label=connect_panel.lan_scan_button)
self.scan.connect("clicked", self._on_scan_clicked) self.scan.connect("clicked", self._on_scan_clicked)
# TODO: strings
self.early_abort = Gtk.CheckButton(label=connect_panel.lan_checkbox) self.early_abort = Gtk.CheckButton(label=connect_panel.lan_checkbox)
self.early_abort.set_active(True) self.early_abort.set_active(True)
self.early_abort.set_tooltip_text(connect_panel.lan_abort_tooltip) self.early_abort.set_tooltip_text(connect_panel.lan_abort_tooltip)
@@ -190,8 +190,7 @@ class FavPanel(Gtk.Frame):
self.fav_button.connect("clicked", self._on_connect_clicked) self.fav_button.connect("clicked", self._on_connect_clicked)
self.copy_button = CopyIpButton(self.controller, self.get_fav_ip) self.copy_button = CopyIpButton(self.controller, self.get_fav_ip)
# TODO: strings self.unset_button = CloseButton(unset_button_label)
self.unset_button = CloseButton("Unset")
self.unset_button.connect("clicked", self._on_unset_clicked) self.unset_button.connect("clicked", self._on_unset_clicked)
if favorite is None: if favorite is None:
+7 -8
View File
@@ -1,9 +1,10 @@
from typing import Any, Callable, TYPE_CHECKING from typing import Any, Callable, TYPE_CHECKING
from dzgui.api.servers import validate_ip from dzgui.api.servers import validate_ip
from dzgui.const.icons import ICON_VIEW_CONCEAL, ICON_VIEW_REVEAL from dzgui.const.constants import VIEW_CONCEAL, VIEW_REVEAL
from dzgui.util.css import add_class, remove_class from dzgui.util.css import add_class, remove_class
from dzgui.strings import connect_panel from dzgui.strings import connect_panel
from dzgui.strings.options import api_placeholder, api_validate
from dzgui.strings.errors import api_popover from dzgui.strings.errors import api_popover
import gi import gi
@@ -143,19 +144,17 @@ class APIEntry(Gtk.Box):
self.func: Callable | None = None self.func: Callable | None = None
# TODO: strings
self.entry = Gtk.Entry( 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, ICON_VIEW_REVEAL) self.entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, VIEW_REVEAL)
self.entry.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, True) self.entry.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, True)
self.entry.set_visibility(False) self.entry.set_visibility(False)
self.entry.connect("changed", self._on_text_changed) self.entry.connect("changed", self._on_text_changed)
self.entry.connect("icon-release", self._on_icon_release) self.entry.connect("icon-release", self._on_icon_release)
self.entry.connect("activate", self._on_field_activated) self.entry.connect("activate", self._on_field_activated)
# TODO: strings self.submit = Gtk.Button(label=api_validate)
self.submit = Gtk.Button(label="Validate")
self.submit.set_sensitive(False) self.submit.set_sensitive(False)
self.submit.connect("clicked", self._on_submit) self.submit.connect("clicked", self._on_submit)
@@ -206,9 +205,9 @@ class APIEntry(Gtk.Box):
) -> None: ) -> None:
visible = widget.get_visibility() visible = widget.get_visibility()
if visible: if visible:
icon, state = ICON_VIEW_REVEAL, False icon, state = VIEW_REVEAL, False
else: else:
icon, state = ICON_VIEW_CONCEAL, True icon, state = VIEW_CONCEAL, True
widget.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) widget.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon)
widget.set_visibility(state) widget.set_visibility(state)
+2 -2
View File
@@ -1,7 +1,7 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from dzgui.const.enum import NotebookPage from dzgui.const.enum import NotebookPage
from dzgui.const.icons import ICON_HELP_BUBBLE from dzgui.const.constants import HELP_BUBBLE
import gi import gi
@@ -20,7 +20,7 @@ class InfoEventBox(Gtk.EventBox):
self.text = text self.text = text
self.icon = Gtk.Image.new_from_icon_name( self.icon = Gtk.Image.new_from_icon_name(
ICON_HELP_BUBBLE, Gtk.IconSize.LARGE_TOOLBAR HELP_BUBBLE, Gtk.IconSize.LARGE_TOOLBAR
) )
self.icon.set_opacity(0.8) self.icon.set_opacity(0.8)
box = Gtk.Box() box = Gtk.Box()
+17 -11
View File
@@ -3,10 +3,19 @@ from typing import Literal, TYPE_CHECKING
from dzgui.const.constants import ( from dzgui.const.constants import (
APP_NAME, 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.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.maps_combo import MapsCombo
from dzgui.views.components.labels import BoldLabel from dzgui.views.components.labels import BoldLabel
@@ -98,8 +107,7 @@ class ButtonGrid(Gtk.Grid):
class KeywordEntry(Gtk.Entry): class KeywordEntry(Gtk.Entry):
def __init__(self, controller: "Controller") -> None: def __init__(self, controller: "Controller") -> None:
# TODO :strings super().__init__(placeholder_text=keyword_placeholder)
super().__init__(placeholder_text="Filter by keyword")
self.keyword = "" self.keyword = ""
self.controller = controller self.controller = controller
@@ -108,7 +116,7 @@ class KeywordEntry(Gtk.Entry):
self.connect("key-press-event", self._on_keypress) self.connect("key-press-event", self._on_keypress)
self.connect("icon-release", lambda *args: self.activate()) self.connect("icon-release", lambda *args: self.activate())
self.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, ICON_SEARCH) self.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, SEARCH_ICON)
self.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, True) self.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, True)
self.emitter.connect("load_maps", self._on_maps_loaded) self.emitter.connect("load_maps", self._on_maps_loaded)
@@ -173,8 +181,7 @@ class FilterPanel(Gtk.Box):
self.keyword_entry = KeywordEntry(self.controller) self.keyword_entry = KeywordEntry(self.controller)
self.button_grid = ButtonGrid(self.controller, defaults, tooltips) self.button_grid = ButtonGrid(self.controller, defaults, tooltips)
# TODO: strings self.filters_label = BoldLabel(filters_heading)
self.filters_label = BoldLabel("Filters")
self.emitter.connect( self.emitter.connect(
"request_keyword_focus", lambda _: self.keyword_entry.grab_focus() "request_keyword_focus", lambda _: self.keyword_entry.grab_focus()
@@ -194,13 +201,12 @@ class FilterPanel(Gtk.Box):
self.maps_entry: Gtk.Entry = self.maps_combo.get_child() # type: ignore self.maps_entry: Gtk.Entry = self.maps_combo.get_child() # type: ignore
self.maps_entry.set_completion(completion) self.maps_entry.set_completion(completion)
# TODO: strings self.maps_entry.set_placeholder_text(map_placeholder)
self.maps_entry.set_placeholder_text("Filter by map")
self.maps_entry.connect("changed", self._on_map_completion, True) 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("key-press-event", self._on_map_entry_keypress)
self.maps_entry.connect("activate", self._on_maps_activated) self.maps_entry.connect("activate", self._on_maps_activated)
self.maps_combo.pack_start(renderer_text, expand=True) self.maps_combo.pack_start(renderer_text, EXPAND)
self.maps_combo.connect("changed", self._on_map_changed) self.maps_combo.connect("changed", self._on_map_changed)
self.maps_combo.connect("key-press-event", self._on_combo_keypress) self.maps_combo.connect("key-press-event", self._on_combo_keypress)
@@ -210,7 +216,7 @@ class FilterPanel(Gtk.Box):
self.maps_combo, self.maps_combo,
self.button_grid, self.button_grid,
): ):
self.pack_start(el, expand=False, fill=False, padding=0) self.pack_start(el, NO_EXPAND, NO_FILL, NO_PADDING)
def _on_maps_activated(self, entry: Gtk.Entry) -> None: def _on_maps_activated(self, entry: Gtk.Entry) -> None:
text = entry.get_text() text = entry.get_text()
+2 -3
View File
@@ -1,8 +1,7 @@
from typing import Self from typing import Self
from warnings import deprecated from warnings import deprecated
from dzgui.const.constants import FLATPAK_RUN_CMD, FLATPAK_SANDBOX, STEAM_CMD from dzgui.const.constants import ERROR, 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.init.prereqs import has_steam_client, has_flatpak_client
from dzgui.strings import options from dzgui.strings import options
from dzgui.views.components.box import HBox from dzgui.views.components.box import HBox
@@ -74,7 +73,7 @@ class ErrorPopover(Gtk.Popover):
self.hbox = HBox() self.hbox = HBox()
self.label = Gtk.Label(label="", margin_start=10, margin_end=10) self.label = Gtk.Label(label="", margin_start=10, margin_end=10)
error_icon = Icon(ICON_ERROR, margin_start=10) error_icon = Icon(ERROR, margin_start=10)
self.hbox.extend([error_icon, self.label]) self.hbox.extend([error_icon, self.label])
self.add(self.hbox) self.add(self.hbox)
self.show_all() self.show_all()
+4 -3
View File
@@ -1,6 +1,7 @@
from typing import Self, TYPE_CHECKING from typing import Self, TYPE_CHECKING
from dzgui.const.enum import ModButton 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.util.strings import mod_panel
from dzgui.views.components.labels import BoldLabel from dzgui.views.components.labels import BoldLabel
@@ -54,7 +55,7 @@ class ModSelectionPanel(Gtk.Box):
for button in buttons: for button in buttons:
b = EnumeratedModButton(button) b = EnumeratedModButton(button)
b.connect("clicked", self._on_button_clicked) b.connect("clicked", self._on_button_clicked)
self.main_panel.pack_start(b, expand=False, fill=True, padding=0) self.main_panel.pack_start(b, NO_EXPAND, FILL, NO_PADDING)
self.stale_panel = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) self.stale_panel = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
self.highlight_stale_button = ModPanelButton(ModButton.HIGHLIGHT_STALE) self.highlight_stale_button = ModPanelButton(ModButton.HIGHLIGHT_STALE)
@@ -66,11 +67,11 @@ class ModSelectionPanel(Gtk.Box):
self.unhighlight_stale_button, self.unhighlight_stale_button,
self.select_stale_button, self.select_stale_button,
): ):
self.stale_panel.pack_start(b, expand=False, fill=True, padding=0) self.stale_panel.pack_start(b, NO_EXPAND, FILL, NO_PADDING)
b.connect("clicked", self._on_button_clicked) b.connect("clicked", self._on_button_clicked)
for el in header, self.main_panel, self.stale_panel: for el in header, self.main_panel, self.stale_panel:
self.pack_start(el, expand=False, fill=True, padding=0) self.pack_start(el, NO_EXPAND, FILL, NO_PADDING)
self.connect("map", self._reinit_button) self.connect("map", self._reinit_button)
+11 -8
View File
@@ -6,9 +6,14 @@ from typing import Literal, TYPE_CHECKING
from dzgui.const.constants import ( from dzgui.const.constants import (
APP_NAME, APP_NAME,
NO_EXPAND,
NO_FILL,
FILL,
NO_PADDING,
) )
from dzgui.const.enum import ServerTab from dzgui.const.enum import ServerTab
from dzgui.managers.update import UpdateManager 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.clip import copy_clipboard
from dzgui.util.file import is_writeable from dzgui.util.file import is_writeable
from dzgui.views.components.buttonbox import ButtonBox from dzgui.views.components.buttonbox import ButtonBox
@@ -61,20 +66,19 @@ class RightPanel(Gtk.Box):
vexpand=True, vexpand=True,
halign=Gtk.Align.END, halign=Gtk.Align.END,
valign=Gtk.Align.END, valign=Gtk.Align.END,
# TODO: strings tooltip_text=version_tooltip,
tooltip_text="Click to copy to clipboard",
) )
eb = Gtk.EventBox(halign=Gtk.Align.END, valign=Gtk.Align.END) eb = Gtk.EventBox(halign=Gtk.Align.END, valign=Gtk.Align.END)
eb.add(self.version_label) eb.add(self.version_label)
eb.connect("button-press-event", self._on_version_clicked) eb.connect("button-press-event", self._on_version_clicked)
for el in self.button_vbox, self.keys, self.filters_vbox, self.refresh_button: for el in self.button_vbox, self.keys, self.filters_vbox, self.refresh_button:
self.pack_start(el, expand=False, fill=True, padding=0) self.pack_start(el, NO_EXPAND, FILL, NO_PADDING)
self.pack_start(self.sel_panel, expand=False, fill=False, padding=0) self.pack_start(self.sel_panel, NO_EXPAND, NO_FILL, NO_PADDING)
self.update_button = IconTextButton( self.update_button = IconTextButton(
"dialog-information-symbolic", label="Updates available" "dialog-information-symbolic", label=update_button
) )
self.gutter_box = Gtk.Box( self.gutter_box = Gtk.Box(
@@ -92,7 +96,7 @@ class RightPanel(Gtk.Box):
"clicked", self._on_update_button_clicked, exe_path, update "clicked", self._on_update_button_clicked, exe_path, update
) )
self.gutter_box.add(eb) self.gutter_box.add(eb)
self.pack_start(self.gutter_box, expand=False, fill=True, padding=0) self.pack_start(self.gutter_box, NO_EXPAND, FILL, NO_PADDING)
def _on_update_button_clicked( def _on_update_button_clicked(
self, button: Gtk.Button, exe_path: str, url: str self, button: Gtk.Button, exe_path: str, url: str
@@ -127,7 +131,6 @@ class RightPanel(Gtk.Box):
return return
self.copying = True self.copying = True
version = self.version_label.get_text() version = self.version_label.get_text()
# TODO: strings self.version_label.set_text(version_notifier)
self.version_label.set_text("Copied!")
copy_clipboard(version) copy_clipboard(version)
GLib.timeout_add_seconds(1, revert) GLib.timeout_add_seconds(1, revert)
+3 -2
View File
@@ -14,6 +14,7 @@ from dzgui.const.constants import HEX_GREEN, HEX_RED
from dzgui.init.coords import get_local_coords from dzgui.init.coords import get_local_coords
from dzgui.init.dayz import is_dayz_installed from dzgui.init.dayz import is_dayz_installed
from dzgui.init.update import check_updates 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.managers.threading import call_on_thread, StoredFunc, ThreadingManager
from dzgui.strings import preboot, dialogs from dzgui.strings import preboot, dialogs
from dzgui.util.format import format_exception from dzgui.util.format import format_exception
@@ -117,8 +118,8 @@ class BootDialog(ColorAwareApp, Gtk.Dialog): # type: ignore
self.error_box.add(self.button_hbox) self.error_box.add(self.button_hbox)
self.content = self.get_content_area() self.content = self.get_content_area()
self.content.pack_start(self.scrollable_tree, expand=True, fill=True, padding=0) self.content.pack_start(self.scrollable_tree, EXPAND, FILL, 0)
self.content.pack_start(self.error_box, expand=True, fill=True, padding=0) self.content.pack_start(self.error_box, EXPAND, FILL, 0)
self.show_all() self.show_all()
self.error_box.hide() self.error_box.hide()
+3 -3
View File
@@ -3,7 +3,7 @@ from dataclasses import dataclass
from datetime import datetime, time, timedelta from datetime import datetime, time, timedelta
from typing import Literal from typing import Literal
from dzgui.const.icons import ICON_DAY, ICON_NIGHT from dzgui.const.constants import DAY_ICON, NIGHT_ICON
from dzgui.strings import calc from dzgui.strings import calc
from dzgui.views.components.icon import Icon from dzgui.views.components.icon import Icon
from dzgui.views.components.labels import BoldLabel from dzgui.views.components.labels import BoldLabel
@@ -385,7 +385,7 @@ class TimePickerFrame(Gtk.Frame):
h1 = BoldLabel(calc.heading_server_time) h1 = BoldLabel(calc.heading_server_time)
h2 = BoldLabel(calc.heading_target_time) h2 = BoldLabel(calc.heading_target_time)
self.icon_indicator = Icon(ICON_DAY) self.icon_indicator = Icon(DAY_ICON)
self.update_server_icon() self.update_server_icon()
server_time_box = HBox(10) server_time_box = HBox(10)
@@ -411,7 +411,7 @@ class TimePickerFrame(Gtk.Frame):
self.add(flowbox) self.add(flowbox)
def update_server_icon(self) -> None: def update_server_icon(self) -> None:
icon = ICON_DAY if self.server_time.is_day() else ICON_NIGHT icon = DAY_ICON if self.server_time.is_day() else NIGHT_ICON
self.icon_indicator.set_icon_name(icon) self.icon_indicator.set_icon_name(icon)
def _on_server_clock_increment(self, emitter: Emitter, time_now: datetime) -> None: def _on_server_clock_increment(self, emitter: Emitter, time_now: datetime) -> None:
+8 -8
View File
@@ -1,5 +1,6 @@
from typing import Literal, Self, TYPE_CHECKING 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.strings import dialogs
from dzgui.util import strings from dzgui.util import strings
from dzgui.views.components.buttons import ClipboardButton from dzgui.views.components.buttons import ClipboardButton
@@ -45,7 +46,7 @@ class GenericDialog(Gtk.MessageDialog):
def pack(self, widget: Gtk.Widget) -> None: def pack(self, widget: Gtk.Widget) -> None:
content = self.get_content_area() content = self.get_content_area()
content.pack_start(widget, expand=True, fill=True, padding=0) content.pack_start(widget, EXPAND, FILL, NO_PADDING)
class ConfirmationDialog(GenericDialog): class ConfirmationDialog(GenericDialog):
@@ -120,13 +121,13 @@ class WaitDialog(GenericDialog):
spinner_box.add(spinner) spinner_box.add(spinner)
if self.jobs > 1: if self.jobs > 1:
content.pack_start(self.prog, expand=False, fill=False, padding=0) content.pack_start(self.prog, NO_EXPAND, NO_FILL, 0)
else: else:
content.pack_start(spinner_box, expand=False, fill=False, padding=0) content.pack_start(spinner_box, NO_EXPAND, NO_FILL, 0)
spinner.start() spinner.start()
if show_cancel is True: if show_cancel is True:
content.pack_start(self.cancel, expand=False, fill=False, padding=0) content.pack_start(self.cancel, NO_EXPAND, NO_FILL, 0)
def update_text(self, msg: str) -> None: def update_text(self, msg: str) -> None:
self.format_secondary_text(msg) self.format_secondary_text(msg)
@@ -139,8 +140,7 @@ class WaitDialog(GenericDialog):
self.cur_job += 1 self.cur_job += 1
def _on_cancel_clicked(self, button: Gtk.Button) -> None: def _on_cancel_clicked(self, button: Gtk.Button) -> None:
# TODO: strings GLib.idle_add(self.update_text, dialogs.caught_cancel_signal)
GLib.idle_add(self.update_text, "Caught cancel signal, cleaning up")
self.controller.set_cancel_event() self.controller.set_cancel_event()
def show_cancel(self, state: bool) -> None: def show_cancel(self, state: bool) -> None:
@@ -271,7 +271,7 @@ class ExceptionDialog(TextBufferDialog):
bottom_margin=10, bottom_margin=10,
) )
textview.set_buffer(Gtk.TextBuffer(text=trace)) textview.set_buffer(Gtk.TextBuffer(text=trace))
box.pack_start(textview, expand=True, fill=True, padding=0) box.pack_start(textview, EXPAND, FILL, 0)
self.error_details = Gtk.ScrolledWindow( self.error_details = Gtk.ScrolledWindow(
overlay_scrolling=False, overlay_scrolling=False,
@@ -295,7 +295,7 @@ class ExceptionDialog(TextBufferDialog):
) )
details_textview.set_buffer(self.details_buffer) details_textview.set_buffer(self.details_buffer)
details_box.pack_start(details_textview, expand=True, fill=True, padding=10) details_box.pack_start(details_textview, EXPAND, FILL, 10)
self.error_details.add(details_box) self.error_details.add(details_box)
self.error_notebook = Gtk.Notebook(show_tabs=False, margin_bottom=15) self.error_notebook = Gtk.Notebook(show_tabs=False, margin_bottom=15)
+2 -1
View File
@@ -1,6 +1,7 @@
import textwrap import textwrap
from typing import Self, TYPE_CHECKING from typing import Self, TYPE_CHECKING
from dzgui.const.constants import NO_EXPAND, NO_FILL
from dzgui.util.strings import notice from dzgui.util.strings import notice
from dzgui.views.dialogs.generic import GenericDialog from dzgui.views.dialogs.generic import GenericDialog
from dzgui.views.components.buttons import SteamTextButton from dzgui.views.components.buttons import SteamTextButton
@@ -41,7 +42,7 @@ class WorkshopLinkDialog(GenericDialog):
button.set_margin_start(60) button.set_margin_start(60)
button.set_margin_end(60) button.set_margin_end(60)
button.connect("clicked", self._on_button_clicked, uid) button.connect("clicked", self._on_button_clicked, uid)
self.dialogBox.pack_end(button, expand=False, fill=False, padding=0) self.dialogBox.pack_end(button, NO_EXPAND, NO_FILL, 0)
self.show_all() self.show_all()
self.connect("response", self._on_dialog_response) self.connect("response", self._on_dialog_response)
+10 -18
View File
@@ -1,12 +1,13 @@
from typing import Self, TYPE_CHECKING from typing import Self, TYPE_CHECKING
from dzgui.strings import notes
from dzgui.views.components.entry import ValidatedEntry from dzgui.views.components.entry import ValidatedEntry
from dzgui.views.dialogs.generic import GenericDialog from dzgui.views.dialogs.generic import GenericDialog
import gi # noqa E402 import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gdk, Gtk # noqa E402 from gi.repository import Gtk
if TYPE_CHECKING: if TYPE_CHECKING:
from dzgui.controllers.mc import Controller from dzgui.controllers.mc import Controller
@@ -14,23 +15,20 @@ if TYPE_CHECKING:
class NoteDialog(GenericDialog): class NoteDialog(GenericDialog):
def __init__(self, controller: "Controller"): def __init__(self, controller: "Controller"):
# TODO: add to strings
title = "Add a short note/reminder."
secondary = "Limit 30 chars"
super().__init__( super().__init__(
controller=controller, controller=controller,
text=title, text=notes.dialog_title,
buttons=Gtk.ButtonsType.NONE, buttons=Gtk.ButtonsType.NONE,
mtype=Gtk.MessageType.INFO, mtype=Gtk.MessageType.INFO,
secondary=secondary, secondary=notes.dialog_subtitle,
) )
self.controller = controller self.controller = controller
delete_button = Gtk.Button(label="Delete note") delete_button = Gtk.Button(label=notes.delete_button)
delete_button.set_sensitive(False) delete_button.set_sensitive(False)
self.add_action_widget(delete_button, Gtk.ResponseType.CANCEL) 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.ok.grab_default()
self.entry = ValidatedEntry(controller, self.validate) self.entry = ValidatedEntry(controller, self.validate)
@@ -42,7 +40,7 @@ class NoteDialog(GenericDialog):
self.entry.set_text(note) self.entry.set_text(note)
if len(note) > 0: if len(note) > 0:
delete_button.set_sensitive(True) 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: elif len(note) == 0:
self.ok.set_sensitive(False) self.ok.set_sensitive(False)
@@ -55,9 +53,6 @@ class NoteDialog(GenericDialog):
self.entry.connect("changed", self._on_text_changed) self.entry.connect("changed", self._on_text_changed)
def _on_text_changed(self, entry: Gtk.Entry) -> None: 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: if len(entry.get_text()) == 0:
self.ok.set_sensitive(False) self.ok.set_sensitive(False)
@@ -71,14 +66,11 @@ class NoteDialog(GenericDialog):
def _on_response(self, dialog: Self, response: Gtk.ResponseType) -> None: def _on_response(self, dialog: Self, response: Gtk.ResponseType) -> None:
match response: match response:
case Gtk.ResponseType.DELETE_EVENT:
self.destroy()
case Gtk.ResponseType.OK: case Gtk.ResponseType.OK:
note = self.entry.get_text() note = self.entry.get_text()
self.controller.add_note(note) self.controller.add_note(note)
self.destroy()
case Gtk.ResponseType.CANCEL: case Gtk.ResponseType.CANCEL:
self.controller.delete_note() self.controller.delete_note()
self.destroy() case _:
case Gtk.ResponseType.CANCEL | Gtk.ResponseType.CLOSE: pass
self.destroy() self.destroy()
+2 -2
View File
@@ -2,7 +2,7 @@ import gi
import html import html
from typing import Self, TYPE_CHECKING from typing import Self, TYPE_CHECKING
from dzgui.const.icons import ICON_CALCULATOR from dzgui.const.constants import CALCULATOR
from dzgui.const.enum import ContextMenuGroup from dzgui.const.enum import ContextMenuGroup
from dzgui.model.model_factory import ModelFactory from dzgui.model.model_factory import ModelFactory
from dzgui.util import css from dzgui.util import css
@@ -110,7 +110,7 @@ class ServerDetailsDialog(ServerDialog):
text = format_hyperlinks(text) text = format_hyperlinks(text)
self.description.set_markup(html.escape(text)) self.description.set_markup(html.escape(text))
calc_button = IconTextButton(ICON_CALCULATOR, dialogs.toggle_calc) calc_button = IconTextButton(CALCULATOR, dialogs.toggle_calc)
calc_button.connect("clicked", self._on_calc_pressed) calc_button.connect("clicked", self._on_calc_pressed)
calc_button.set_halign(Gtk.Align.END) calc_button.set_halign(Gtk.Align.END)
calc_button.set_valign(Gtk.Align.START) calc_button.set_valign(Gtk.Align.START)
+1 -1
View File
@@ -14,7 +14,7 @@ from dzgui.const.constants import (
HERO_PATH, HERO_PATH,
FLATPAK_ROOT_PATH, FLATPAK_ROOT_PATH,
LEGACY_CONFIG_PATH, LEGACY_CONFIG_PATH,
TMP_PATH TMP_PATH,
) )
from dzgui.const.boilerplate import config_boilerplate from dzgui.const.boilerplate import config_boilerplate
from dzgui.const.endpoints import STEAM_API_SETUP from dzgui.const.endpoints import STEAM_API_SETUP
+6 -4
View File
@@ -3,12 +3,14 @@ from enum import Enum
from typing import Self, TYPE_CHECKING, Union from typing import Self, TYPE_CHECKING, Union
from dzgui.util import css from dzgui.util import css
from dzgui.const.icons import ICON_EDIT_DELETE, ICON_FOLDER, ICON_WARNING
from dzgui.const.constants import ( from dzgui.const.constants import (
APPID_DAYZ, APPID_DAYZ,
APPID_DAYZ_EXP, APPID_DAYZ_EXP,
APPNAME_DAYZ, APPNAME_DAYZ,
APPNAME_DAYZ_EXP_HUMAN, APPNAME_DAYZ_EXP_HUMAN,
EDIT_DELETE,
FOLDER,
WARNING,
) )
from dzgui.const.enum import ContextMenuGroup, NotebookPage from dzgui.const.enum import ContextMenuGroup, NotebookPage
from dzgui.managers.offline import OfflineManager from dzgui.managers.offline import OfflineManager
@@ -85,7 +87,7 @@ class FolderHBox(HBox):
self.emitter = controller.get_emitter() self.emitter = controller.get_emitter()
self.eb = InfoEventBox(eb_text, controller) self.eb = InfoEventBox(eb_text, controller)
self.button = IconTextButton(ICON_FOLDER, btn_label, Gtk.PositionType.LEFT) self.button = IconTextButton(FOLDER, btn_label, Gtk.PositionType.LEFT)
self.button.set_halign(Gtk.Align.START) self.button.set_halign(Gtk.Align.START)
self.button.set_image_position(Gtk.PositionType.LEFT) self.button.set_image_position(Gtk.PositionType.LEFT)
@@ -97,7 +99,7 @@ class FolderHBox(HBox):
self.label = Gtk.Label() self.label = Gtk.Label()
self.scrolled_label.add(self.label) self.scrolled_label.add(self.label)
self.unset_button = IconTextButton(ICON_EDIT_DELETE, offline.unset_button) self.unset_button = IconTextButton(EDIT_DELETE, offline.unset_button)
self.unset_button.connect("clicked", self._on_unset_clicked) self.unset_button.connect("clicked", self._on_unset_clicked)
self.spinner = Gtk.Spinner() self.spinner = Gtk.Spinner()
@@ -193,7 +195,7 @@ class ModFrame(HeadingFrame):
self.tree_vbox.extend([self.scrolled, self.status]) self.tree_vbox.extend([self.scrolled, self.status])
self.no_mods = HBox(spacing=10) self.no_mods = HBox(spacing=10)
no_mods_icon = Icon(ICON_WARNING, margin_start=10) no_mods_icon = Icon(WARNING, margin_start=10)
no_mods_label = Gtk.Label(label=offline.no_local_mods, halign=Gtk.Align.START) no_mods_label = Gtk.Label(label=offline.no_local_mods, halign=Gtk.Align.START)
self.no_mods.extend([no_mods_icon, no_mods_label]) self.no_mods.extend([no_mods_icon, no_mods_label])
+10 -6
View File
@@ -13,8 +13,12 @@ from dzgui.const.constants import (
FLATPAK_RUN_CMD, FLATPAK_RUN_CMD,
FLATPAK_SANDBOX, FLATPAK_SANDBOX,
FLATPAK_STEAM_PATH, 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.endpoints import STEAM_API_SETUP
from dzgui.const.enum import Preferences, ServerTab from dzgui.const.enum import Preferences, ServerTab
from dzgui.strings import options from dzgui.strings import options
@@ -63,7 +67,7 @@ class SubmitField(Gtk.Box):
# TODO: audit for all applicable callbacks # TODO: audit for all applicable callbacks
if private: if private:
self.entry.set_icon_from_icon_name( self.entry.set_icon_from_icon_name(
Gtk.EntryIconPosition.SECONDARY, ICON_VIEW_REVEAL Gtk.EntryIconPosition.SECONDARY, VIEW_REVEAL
) )
self.entry.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, True) self.entry.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, True)
self.entry.connect("icon-release", self._on_icon_release) self.entry.connect("icon-release", self._on_icon_release)
@@ -127,9 +131,9 @@ class SubmitField(Gtk.Box):
) -> None: ) -> None:
visible = widget.get_visibility() visible = widget.get_visibility()
if visible: if visible:
icon, state = ICON_VIEW_REVEAL, False icon, state = VIEW_REVEAL, False
else: else:
icon, state = ICON_VIEW_CONCEAL, True icon, state = VIEW_CONCEAL, True
widget.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) widget.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon)
widget.set_visibility(state) widget.set_visibility(state)
@@ -230,8 +234,8 @@ class ToggleField(Gtk.Box):
self.radio1 = Gtk.RadioButton.new_with_label(None, first_option) self.radio1 = Gtk.RadioButton.new_with_label(None, first_option)
self.radio2 = Gtk.RadioButton.new_from_widget(self.radio1) self.radio2 = Gtk.RadioButton.new_from_widget(self.radio1)
self.radio2.set_label(second_option) self.radio2.set_label(second_option)
self.pack_start(self.radio1, expand=False, fill=False, padding=0) self.pack_start(self.radio1, NO_EXPAND, NO_FILL, NO_PADDING)
self.pack_start(self.radio2, expand=False, fill=False, padding=0) self.pack_start(self.radio2, NO_EXPAND, NO_FILL, NO_PADDING)
def set_suboption_active(self, state: bool) -> None: def set_suboption_active(self, state: bool) -> None:
# NOTE: defer connection of signal until after state is set # NOTE: defer connection of signal until after state is set
+5 -5
View File
@@ -1,9 +1,9 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import Self, Sequence, TYPE_CHECKING from typing import Self, Sequence, TYPE_CHECKING
from dzgui.const.icons import ( from dzgui.const.constants import (
ICON_ERROR, ERROR,
ICON_WARNING, WARNING,
) )
from dzgui.const.enum import NotebookPage from dzgui.const.enum import NotebookPage
from dzgui.util.css import add_class from dzgui.util.css import add_class
@@ -166,7 +166,7 @@ class PreConnectionAssistant(Gtk.Box):
# TODO: abstract into components # TODO: abstract into components
self.warning_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.warning_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.warning_tree = MaskedTree(ICON_WARNING) self.warning_tree = MaskedTree(WARNING)
self.warning_placeholder = Placeholder(preconnect.placeholder_no_warnings) self.warning_placeholder = Placeholder(preconnect.placeholder_no_warnings)
self.warning_box.add(self.warning_tree) self.warning_box.add(self.warning_tree)
self.warning_box.add(self.warning_placeholder) 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_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.error_tree = MaskedTree(ICON_ERROR) self.error_tree = MaskedTree(ERROR)
self.error_placeholder = Placeholder(preconnect.no_errors) self.error_placeholder = Placeholder(preconnect.no_errors)
self.error_box.add(self.error_tree) self.error_box.add(self.error_tree)
self.error_box.add(self.error_placeholder) self.error_box.add(self.error_placeholder)
+2 -2
View File
@@ -5,6 +5,7 @@ from typing import Self, TYPE_CHECKING
from dzgui.const.constants import APP_NAME from dzgui.const.constants import APP_NAME
from dzgui.const.enum import ContextMenuGroup, ServerTab from dzgui.const.enum import ContextMenuGroup, ServerTab
from dzgui.views.trees.tree_servers import ServerTreeView from dzgui.views.trees.tree_servers import ServerTreeView
from dzgui.strings.generic import tab_changed_label
from dzgui.util.strings import server_labels from dzgui.util.strings import server_labels
import gi import gi
@@ -147,8 +148,7 @@ class ServerNotebook(Gtk.ScrolledWindow):
label = self.notebook.get_tab_label_text(child) label = self.notebook.get_tab_label_text(child)
if label is not None: if label is not None:
# TODO: strings text = label.strip(tab_changed_label)
text = label.strip("*")
self.notebook.set_tab_label_text(child, text) self.notebook.set_tab_label_text(child, text)
tree = child.get_tree() tree = child.get_tree()
+2 -2
View File
@@ -1,4 +1,4 @@
from shared import get_git_root from pathlib import Path
BOILERPLATE = """ BOILERPLATE = """
This file includes the combined licenses to the following components: This file includes the combined licenses to the following components:
@@ -40,7 +40,7 @@ class CombinedLicense:
def concat_license() -> str: def concat_license() -> str:
combined = CombinedLicense() combined = CombinedLicense()
root = get_git_root() root = Path(__file__).resolve().parents[1]
licenses = root.joinpath("docs/licenses") licenses = root.joinpath("docs/licenses")
main_license = root.joinpath(LICENSE_FILE) main_license = root.joinpath(LICENSE_FILE)
+4 -5
View File
@@ -1,11 +1,10 @@
import shutil import shutil
import subprocess import subprocess
from pathlib import Path
from shared import get_git_root par = Path(__file__).parent.parent
clog = par / "CHANGELOG.md"
root = get_git_root() target = par / "dzgui/data/CHANGELOG.md"
clog = root.joinpath("CHANGELOG.md")
target = root.joinpath("dzgui/data/CHANGELOG.md")
shutil.copy(clog, target) shutil.copy(clog, target)
+3 -2
View File
@@ -1,9 +1,10 @@
import tomllib import tomllib
from shared import get_git_root from pathlib import Path
root = Path(__file__).resolve().parents[1]
def get_entrypoint() -> None: def get_entrypoint() -> None:
root = get_git_root()
p = root.joinpath("pyproject.toml") p = root.joinpath("pyproject.toml")
with open(p, "rb") as f: with open(p, "rb") as f:
t = tomllib.load(f) t = tomllib.load(f)
+2 -3
View File
@@ -3,15 +3,14 @@ import requests
import subprocess import subprocess
import tarfile import tarfile
from pathlib import Path
from urllib.parse import unquote from urllib.parse import unquote
from shared import get_git_root
BUILD = "3.13.9" BUILD = "3.13.9"
DATE = "20251014" DATE = "20251014"
CPYTHON_BUILD = f"cpython-{BUILD}" CPYTHON_BUILD = f"cpython-{BUILD}"
root = get_git_root() root = Path(__file__).resolve().parents[1]
build_dir = root.joinpath("build") build_dir = root.joinpath("build")
cpython_dir = build_dir.joinpath(CPYTHON_BUILD) cpython_dir = build_dir.joinpath(CPYTHON_BUILD)
+1 -2
View File
@@ -9,7 +9,6 @@ from pathlib import Path
from concat_licenses import concat_license from concat_licenses import concat_license
from parse_toml import get_entrypoint from parse_toml import get_entrypoint
from prebuild import get_pyapp, rebuild_cpython from prebuild import get_pyapp, rebuild_cpython
from shared import get_git_root
def update_uname(info: tarfile.TarInfo) -> tarfile.TarInfo: def update_uname(info: tarfile.TarInfo) -> tarfile.TarInfo:
@@ -18,7 +17,7 @@ def update_uname(info: tarfile.TarInfo) -> tarfile.TarInfo:
return info return info
root = get_git_root() root = Path(__file__).resolve().parents[1]
dist_dir = root.joinpath("dist") dist_dir = root.joinpath("dist")
build_dir = root.joinpath("build") build_dir = root.joinpath("build")
+10 -1
View File
@@ -1,7 +1,16 @@
import subprocess
from pathlib import Path from pathlib import Path
from dzgui.app_init import build_gui from dzgui.app_init import build_gui
from shared import get_git_root
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())
def get_image_path() -> Path: def get_image_path() -> Path:
-11
View File
@@ -1,11 +0,0 @@
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())