Merge pull request #238 from aclist/doc/port-contributing
Some checks failed
Mirror to Codeberg / mirror-to-codeberg (push) Has been cancelled

Doc/port contributing
This commit is contained in:
aclist 2025-10-30 11:50:23 +09:00 committed by GitHub
commit adf8a62e8a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 363 additions and 94 deletions

View File

@ -1,18 +1,51 @@
# Changelog
## [5.6.6] 2024-02-09
## [5.8.0] 2025-07-06
## Added
- Filter servers by official/unofficial status
- Automatically fetch geolocation records
## Changed
- Updated internal versioning of helper files
## Fixed
- Corrected erroneous 2024 date in prior changelog entries
## [5.7.0] 2025-04-17
## Added
- Save application dimensions when quitting and restore on subsequent boot
## Fixed
- Issues with the window exceeding the bounds of the screen when displaying some table contexts
## Changed
- More direct server ping query method that should return marginally more accurate times
## [5.6.7] 2025-04-04
## Dropped
- Removed extraneous pre-boot API checks that could cause error messages to be printed if the user had not set up an API key yet
## [5.6.6] 2025-03-16
## Changed
- Update IP database records for 2025-03
## [5.6.5] 2025-03-04
## Fixed
- Livonia server results being dropped from batch queries
## [5.6.4] 2025-02-10
### Changed
- Drop launch flag and check for invocation through Steam automatically
## [5.6.3] 2025-02-09
### Changed
- Update IP database records for 2025-02
## [5.6.2] 2024-01-22
## [5.6.2] 2025-01-22
### Fixed
- Resolve regression introduced with IP resolution feature in 5.6.0 (restores functionality of right-click action: Add to My Servers)
## [5.6.1] 2024-01-10
## [5.6.1] 2025-01-10
### Fixed
- Add fallback support for jq 1.6
## [5.6.0] 2024-01-06
## [5.6.0] 2025-01-06
### Added
- Application header bar and controls
- Menu context subtitle in header bar

71
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,71 @@
# Introduction
Thank you for your interest in DZGUI!
This guide goes over development conventions and best practices for contributors.
If you are a developer, you can skip to the end.
# Requesting help
If you encounter a problem with DZGUI, you can submit tickets on the GitHub
(issue tracker)[https://github.com/aclist/dzgui-devel/issues] under the
`troubleshooting` tag.
# How can I help the project?
There are several ways to help this project.
1. Report bugs that you find
2. Request features that you would like
3. Contribute features and fixes to the codebase
4. Contribute documentation to the project
Before making any contribution, please read the `CODE_OF_CONDUCT.md` and act
accordingly.
## Submitting a ticket
Navigate to the GitHub (issue tracker)[https://github.com/aclist/dzgui-devel/issues].
From there, follow the onscreen prompts. You will be asked questions such as:
- What version are you using?
- What distribution are you using?
- What is the issue that you found?
- How can we reproduce the issue?
You can also attach screenshots, logs, or other data that can help us.
## Requesting a feature
You can also request features via the same issue tracker. It is good practice to
first search for your idea to see if a similar one has already been posted.
If not, open a ticket where you describe your feature and its possible benefits.
Please note that this is a community project, so it takes time for us to develop
features. Putting in a feature request does not mean that it will be implemented,
but we will do our best to support as many cool ideas as possible.
## Contributing code or documentation
If you would like to take ownership or assist with an issue on the issue tracker, or
contribute a new change, please follow the guidelines below.
Fork this repository and check out the code up to `prerelease/<latest-version>`.
If there is no pending prerelease PR, you can base your changes off of
the `testing` branch. You may later be asked to retarget your PR to the prerelease
branch once a new one is made. This is because out-of-band PRs are consolidated into
the next release before merging.
The following naming conventions apply for PRs:
- fix/<your-fix> - patch/hotfix branches
- feat/<your-feature> - feature branches
- doc/<your-doc-branch> - documentation branches
- infra/<your-infra-branch> - infrastructure branches
Implement your changes and test them locally. If they work you may
open a merge request, and then we review your changes. If everything is OK, it
will be merged to `prerelease/<latest-version>`, then `testing`, and after a live
testing phase, to `stable` as well.
It is recommended to follow
(Conventional Commits)[https://www.conventionalcommits.org/en/v1.0.0/], as this
integrates well with tooling and helps the project review your code.

150
dzgui.sh
View File

@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -o pipefail
version=5.6.3
version=5.8.0
#CONSTANTS
aid=221100
@ -49,10 +49,9 @@ freedesktop_path="$HOME/.local/share/applications"
#HELPERS
ui_helper="$helpers_path/ui.py"
geo_file="$helpers_path/ips.csv"
km_helper="$helpers_path/latlon"
sums_path="$helpers_path/sums.md5"
func_helper="$helpers_path/funcs"
geo_helper="$helpers_path/ips.csv"
#REMOTE
remote_host=gh
@ -63,7 +62,6 @@ stable_url="$url_prefix/dzgui"
testing_url="$url_prefix/testing"
releases_url="https://github.com/$author/$repo/releases/download/browser"
km_helper_url="$releases_url/latlon"
geo_file_url="$releases_url/ips.csv.gz"
set_im_module(){
@ -587,10 +585,10 @@ fetch_helpers_by_sum(){
[[ -f "$config_file" ]] && source "$config_file"
declare -A sums
sums=(
["ui.py"]="5a876efacf208d12b5fe761996425412"
["ui.py"]="f128a97e744e9e11036d707198feb8a8"
["query_v2.py"]="55d339ba02512ac69de288eb3be41067"
["vdf2json.py"]="2f49f6f5d3af919bebaab2e9c220f397"
["funcs"]="417bd5eaffbefc905a843985c691dc64"
["funcs"]="94287c75a5ae0a06a95dd439711e6fba"
["lan"]="c62e84ddd1457b71a85ad21da662b9af"
)
local author="aclist"
@ -637,18 +635,8 @@ fetch_helpers_by_sum(){
done
return 0
}
fetch_geo_file(){
# for binary releases
local geo_sum="9824e9b9a75a4830a2423932cc188b06"
fetch_km_helper(){
local km_sum="b038fdb8f655798207bd28de3a004706"
local gzip="$helpers_path/ips.csv.gz"
if [[ ! -f $geo_file ]] || [[ $(get_hash $geo_file) != $geo_sum ]]; then
local res=$(get_response_code "$geo_file_url")
[[ $res -ne 200 ]] && raise_error_and_quit "Remote resource unavailable: '$geo_file_url'"
curl -Ls "$geo_file_url" > "$gzip"
#force overwrite
gunzip -f "$gzip"
fi
if [[ ! -f $km_helper ]] || [[ $(get_hash $km_helper) != $km_sum ]]; then
local res=$(get_response_code "$km_helper_url")
[[ $res -ne 200 ]] && raise_error_and_quit "Remote resource unavailable: '$km_helper_url'"
@ -656,10 +644,119 @@ fetch_geo_file(){
chmod +x "$km_helper"
fi
}
get_response_code(){
local url="$1"
curl -Ls -I -o /dev/null -w "%{http_code}" "$url"
}
raise_error_and_quit(){
echo "$1"
exit 1
}
fetch_ip_db(){
parse_dl_url(){
curl -Ls "$url" \
| grep "csv.gz" \
| awk -F"['']" '{print $2}'
}
parse_dl_url_date(){
local url="$1"
<<< "$url" \
awk -F/ '{print $5}' \
| awk -F"dbip-city-lite-" '{print $2}' \
| awk -F'.csv.gz' '{print $1}'
}
fetch(){
logger INFO "Triggering fetch routine"
local url="$1"
curl -Ls "$url" > "$base_file"
if [[ $? -ne 0 ]]; then
logger WARN "Abnormal exit while accessing '$url'"
return
fi
gunzip -f "$base_file"
if [[ $? -ne 0 ]]; then
rm "$base_file"
logger WARN "Abnormal exit while unpacking gzip '$base_file'"
return
fi
< "$extracted_file" grep -vE "^[a-z0-9]{4}:" | grep -v "::" > "$ip_file"
if [[ $? -ne 0 ]]; then
logger WARN "Abnormal exit while parsing IPs"
return
fi
rm "${state_path}/${month}.csv"
readarray -t records < <(cat $ip_file | awk -F, 'NR==1 {print $1} END {print $1}')
if [[ ${records[0]} != "0.0.0.0" ]] && [[ ${records[1]} != "224.0.0.0" ]]; then
logger WARN "Anchor records missing in '$ip_file', possibly malformed"
rm "$ip_file"
return
fi
mv "$ip_file" "$geo_helper"
if [[ $? -ne 0 ]]; then
logger WARN "Abnormal exit while moving '$ip_file' to '$helpers_path'"
rm "$ip_file"
fi
echo "$month" > "$month_file"
logger INFO "Wrote '$month' to stub '$month_file'"
logger INFO "Updated '$ip_file'"
}
local url="https://db-ip.com/db/download/ip-to-city-lite"
local month_file="${state_path}/.month"
local ip_file="${state_path}/ips.csv"
# test main url
local res=$(get_response_code "$url")
if [[ $res -ne 200 ]]; then
logger WARN "Failed to retrieve remote resource: '$url' ($res)"
return
fi
logger INFO "Resolved remote URL: '$url'"
# test dl url
local dl_url="$(parse_dl_url)"
local res=$(get_response_code "$dl_url")
if [[ $res -ne 200 ]]; then
logger WARN "Remote resource unavailable: '$dl_url' ($res)"
return
fi
logger INFO "Resolved download URL: '$dl_url'"
local month=$(parse_dl_url_date "$dl_url")
local base_file="${state_path}/${month}.csv.gz"
local extracted_file="${state_path}/${month}.csv"
# no stub file
if [[ ! -f $month_file ]]; then
logger WARN "No stub file '$month_file' present"
fetch "$dl_url"
return
fi
# local needs update
local last_month=$(< "$month_file")
if [[ $last_month != "$month" ]]; then
logger WARN "Local stub '$last_month' does not match remote stub '$month'"
fetch "$dl_url"
return
fi
# if stub is same date, abort
logger INFO "Local stub '$last_month' is identical to remote, skipping"
}
fetch_helpers(){
fetch_a2s
fetch_dzq
fetch_geo_file
fetch_km_helper
fetch_ip_db
fetch_helpers_by_sum
[[ ! -f $share_path/icon.png ]] && freedesktop_dirs
fetch_icons
@ -876,21 +973,14 @@ get_response_code(){
curl -Ls -I -o /dev/null -w "%{http_code}" "$url"
}
test_connection(){
source "$config_file"
declare -A hr
local res1
local res2
local str="No connection could be established to the remote server"
hr=(
["steampowered.com"]="https://api.steampowered.com/IGameServersService/GetServerList/v1/?key=$steam_api"
["github.com"]="https://github.com/$author"
["codeberg.org"]="https://codeberg.org/$author"
)
# steam API is mandatory, except on initial setup
if [[ -n $steam_api ]]; then
res=$(get_response_code "${hr["steampowered.com"]}")
[[ $res -ne 200 ]] && raise_error_and_quit "$str ("steampowered.com")"
fi
res=$(get_response_code "${hr["github.com"]}")
if [[ $res -ne 200 ]]; then
@ -906,7 +996,6 @@ test_connection(){
stable_url="$url_prefix/dzgui"
testing_url="$url_prefix/testing"
km_helper_url="$releases_url/latlon"
geo_file_url="$releases_url/ips.csv.gz"
fi
}
legacy_cols(){
@ -922,14 +1011,14 @@ stale_mod_signatures(){
readarray -t old_mod_ids < <(awk -F, '{print $1}' $versions_file)
for ((i=0; i<${#old_mod_ids[@]}; ++i)); do
if [[ ! -d $workshop_dir/${old_mod_ids[$i]} ]]; then
"$HOME/.local/share/$app_name/helpers/funcs" "align_local" "${old_mod_ids[$i]}"
"$func_helper" "align_local" "${old_mod_ids[$i]}"
fi
done
fi
}
create_new_links(){
"$HOME/.local/share/$app_name/helpers/funcs" "update_symlinks"
"$func_helper" "update_symlinks"
}
initial_setup(){
setup_dirs
@ -1007,10 +1096,7 @@ main(){
uninstall &&
exit 0
fi
if [[ $1 == "--steam" ]] || [[ $1 == "-s" ]]; then
export STEAM_LAUNCH=1
fi
set_im_module
printf "Initializing setup...\n"

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash
set -o pipefail
version=5.6.0
version="5.8.0"
#CONSTANTS
aid=221100
@ -59,7 +59,6 @@ geo_file="$helpers_path/ips.csv"
km_helper="$helpers_path/latlon"
sums_path="$helpers_path/sums.md5"
query_helper="$helpers_path/query_v2.py"
func_helper="$helpers_path/funcs"
lan_helper="$helpers_path/lan"
#STEAM PATHS
@ -453,10 +452,15 @@ get_dist(){
}
get_remote_servers(){
params=(
"\\nor\1\map\chernarusplus\\nor\1\map\sakhal"
"\\nor\1\map\chernarusplus\\nor\1\map\sakhal\\nor\1\map\enoch\empty\1\\nor\1\map\namalsk"
"\map\namalsk\empty\1"
"\map\namalsk\noplayers\1"
"\map\chernarusplus\empty\1"
"\map\chernarusplus\noplayers\1"
"\map\\sakhal"
"\map\\sakhal\empty\1"
"\map\\sakhal\noplayers\1"
"\map\\enoch\empty\1"
"\map\\enoch\noplayers\1"
)
local limit=10000
local url="https://api.steampowered.com/IGameServersService/GetServerList/v1/?"
@ -601,6 +605,20 @@ filter_servers(){
<<< "$filtered" awk -F$separator '!seen[$1]++'
fi
}
filter_official(){
if [[ ${filters[*]} =~ Official ]]; then
echo -n "$filtered"
else
<<< "$filtered" awk -F$separator '$10 == "Community"'
fi
}
filter_community(){
if [[ ${filters[*]} =~ Unoffic ]]; then
echo -n "$filtered"
else
<<< "$filtered" awk -F$separator '$10 == "Official"'
fi
}
filtered=$(filter_perspective)
filtered=$(filter_full)
@ -611,6 +629,8 @@ filter_servers(){
filtered=$(filter_ascii)
filtered=$(filter_duplicates)
filtered=$(filter_keyword)
filtered=$(filter_official)
filtered=$(filter_community)
if [[ -z "$filtered" ]]; then
logger WARN "Filter result is empty"
@ -648,7 +668,8 @@ parse_server_json(){
"\(.max_players)␞" +
"\(if .gametype == null then "0" elif .gametype|split("lqs")[1] == null then "0" else .gametype|split("lqs")[1]|split(",")[0] end)␞" +
"\(.addr|split(":")[0]):\(if .gameport == null then "XXXX" else .gameport end)␞" +
"\(.addr|split(":")[1])"
"\(.addr|split(":")[1])␞" +
"\(if .gametype == null then "null" else (.gametype|split(",")|if any(. == "external") then "Community" else "Official" end) end)"
' | sort -k1
}
align_versions_file(){
@ -780,12 +801,17 @@ logger(){
}
test_ping(){
shift
local time
local res
local ip="$1"
local qport="$2"
local res
res=$(ping -c1 -4 -W0.5 $1 | grep time= | awk -F= '{print $4}')
[[ ! $? -eq 0 ]] && res="Timed out"
printf "%s" "$res"
res=$(a2s $ip $qport info)
if [[ -z $res ]]; then
time="Timed out"
else
time=$(<<< "$res" jq -r '.[].ping')
fi
printf "%s" "$time"
}
show_server_modlist(){
shift
@ -1271,7 +1297,7 @@ symlinks(){
link="@$encoded_id"
mod="${arr[$i]}"
logger INFO "Creating link '$game_dir/$link' for '$workshop_dir/$mod'"
[[ $STEAM_LAUNCH -eq 1 ]] && echo "# Creating mod link $((i+1))/${#arr[@]}"
[[ -n $STEAMSCRIPT ]] && echo "# Creating mod link $((i+1))/${#arr[@]}"
ln -s "$workshop_dir/$mod" "$game_dir/$link"
done
}
@ -1286,7 +1312,7 @@ symlinks(){
if [[ ${#links[@]} -eq 0 ]]; then
logger INFO "No symlinks present in '$game_dir', creating them"
if [[ $STEAM_LAUNCH -eq 1 ]]; then
if [[ -n $STEAMSCRIPT ]]; then
_create_links "${mods[@]}" > >(_pulse)
else
_create_links "${mods[@]}"
@ -1304,7 +1330,7 @@ symlinks(){
# update missing targets
logger INFO "Found ${#hits[@]} unlinked mods"
if [[ $STEAM_LAUNCH -eq 1 ]]; then
if [[ -n $STEAMSCRIPT ]]; then
_create_links "${hits[@]}" > >(_pulse)
else
_create_links "${hits[@]}"

View File

@ -17,7 +17,6 @@ import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib, Gdk, GObject, Pango
# 5.6.0
app_name = "DZGUI"
cache = {}
@ -38,7 +37,7 @@ mod_store = Gtk.ListStore(str, str, str, float, str)
#cf. log_cols
log_store = Gtk.ListStore(str, str, str, str)
#cf. browser_cols
server_store = Gtk.ListStore(str, str, str, str, int, int, int, str, int)
server_store = Gtk.ListStore(str, str, str, str, int, int, int, str, int, str)
default_tooltip = "Select a row to see its detailed description"
server_tooltip = [None, None]
@ -50,6 +49,7 @@ helpers_path = '%s/.local/share/dzgui/helpers' %(user_path)
log_path = '%s/logs' %(state_path)
changelog_path = '%s/CHANGELOG.md' %(state_path)
geometry_path = '%s/dzg.cols.json' %(state_path)
res_path = '%s/dzg.res.json' %(state_path)
funcs = '%s/funcs' %(helpers_path)
mods_temp_file = '%s/dzg.mods_temp' %(cache_path)
stale_mods_temp_file = '%s/dzg.stale_mods_temp' %(cache_path)
@ -88,14 +88,16 @@ log_cols = [
]
filters = {
"1PP": True,
"3PP": True,
"Day": True,
"Night": True,
"Empty": False,
"3PP": True,
"Night": True,
"Full": False,
"Low pop": True,
"Non-ASCII": False,
"Duplicate": False
"Duplicate": False,
"Official": True,
"Unoffic.": True,
}
@ -552,7 +554,7 @@ def parse_server_rows(data):
lines = data.stdout.splitlines()
reader = csv.reader(lines, delimiter=delimiter)
try:
rows = [[row[0], row[1], row[2], row[3], int(row[4]), int(row[5]), int(row[6]), row[7], int(row[8])] for row in reader if row]
rows = [[row[0], row[1], row[2], row[3], int(row[4]), int(row[5]), int(row[6]), row[7], int(row[8]), row[9]] for row in reader if row]
except IndexError:
return 1
for row in rows:
@ -685,7 +687,7 @@ def process_shell_return_code(transient_parent, msg, code, original_input):
process_tree_option([treeview.view, RowType.HANDSHAKE], treeview)
case 255:
spawn_dialog(transient_parent, "Update complete. Please close DZGUI and restart.", Popup.NOTIFY)
Gtk.main_quit()
save_res_and_quit(transient_parent)
def process_tree_option(input, treeview):
@ -874,19 +876,36 @@ class OuterWindow(Gtk.Window):
self.add(self.grid)
if is_game_mode is True:
self.fullscreen()
elif query_config(None, "fullscreen")[0] == "true":
logger.info("User preference for 'fullscreen' is 'true'")
self.maximize()
else:
if query_config(None, "fullscreen")[0] == "true":
self.maximize()
if os.path.isfile(res_path):
with open(res_path, "r") as infile:
try:
data = json.load(infile)
valid_json = True
except json.decoder.JSONDecodeError:
logger.critical("JSON decode error in '%s'" %(res_path))
valid_json = False
else:
valid_json = False
if valid_json:
res = data["res"]
w = res["width"]
h = res["height"]
logger.info("Restoring window size to %s,%s" %(w,h))
self.set_default_size(w, h)
# Hide FilterPanel on main menu
self.show_all()
# Hide FilterPanel on main menu
self.grid.right_panel.set_filter_visibility(False)
self.grid.sel_panel.set_visible(False)
self.grid.scrollable_treelist.treeview.grab_focus()
def halt_proc_and_quit(self, window, event):
self.grid.terminate_treeview_process()
Gtk.main_quit()
save_res_and_quit(window)
class ScrollableTree(Gtk.ScrolledWindow):
@ -896,7 +915,6 @@ class ScrollableTree(Gtk.ScrolledWindow):
self.treeview = TreeView(is_steam_deck)
self.add(self.treeview)
class RightPanel(Gtk.Box):
def __init__(self, is_steam_deck):
super().__init__(spacing=6)
@ -925,8 +943,7 @@ class RightPanel(Gtk.Box):
self.question_button.connect("clicked", self._on_button_clicked)
self.pack_start(self.debug_toggle, False, True, 0)
if is_steam_deck is False:
self.pack_start(self.question_button, False, True, 0)
self.pack_start(self.question_button, False, True, 0)
def _on_button_toggled(self, button, command):
grid = self.get_parent()
@ -971,7 +988,7 @@ class ButtonBox(Gtk.Box):
button.set_opacity(0.6)
self.buttons.append(button)
button.connect("clicked", self._on_selection_button_clicked)
self.pack_start(button, False, False, True)
self.pack_start(button, False, False, 0)
self.buttons[0].set_opacity(1.0)
@ -1043,7 +1060,9 @@ class ButtonBox(Gtk.Box):
if context == ButtonType.EXIT:
logger.info("Normal user exit")
Gtk.main_quit()
widgets = relative_widget(self)
window = widgets["outer"]
save_res_and_quit(window)
return
cols = treeview.get_columns()
@ -1073,12 +1092,13 @@ class ButtonBox(Gtk.Box):
class CalcDist(multiprocessing.Process):
def __init__(self, widget, addr, result_queue, cache):
def __init__(self, widget, addr, qport, result_queue, cache):
super().__init__()
self.widget = widget
self.result_queue = result_queue
self.addr = addr
self.qport = str(qport)
self.ip = addr.split(':')[0]
def run(self):
@ -1087,7 +1107,7 @@ class CalcDist(multiprocessing.Process):
self.result_queue.put([self.addr, cache[self.addr][0], cache[self.addr][1]])
return
proc = call_out(self.widget, "get_dist", self.ip)
proc2 = call_out(self.widget, "test_ping", self.ip)
proc2 = call_out(self.widget, "test_ping", self.ip, self.qport)
km = proc.stdout
ping = proc2.stdout
self.result_queue.put([self.addr, km, ping])
@ -1314,6 +1334,7 @@ class TreeView(Gtk.TreeView):
if self.view == WindowContext.TABLE_API or self.view == WindowContext.TABLE_SERVER:
addr = self.get_column_at_index(7)
qport = self.get_column_at_index(8)
if addr is None:
server_tooltip[0] = format_tooltip()
grid.update_statusbar(server_tooltip[0])
@ -1327,7 +1348,7 @@ class TreeView(Gtk.TreeView):
grid.update_statusbar(tooltip)
return
self.emit("on_distcalc_started")
self.current_proc = CalcDist(self, addr, self.queue, cache)
self.current_proc = CalcDist(self, addr, qport, self.queue, cache)
self.current_proc.start()
else:
tooltip = format_metadata(row_sel)
@ -1582,13 +1603,15 @@ class TreeView(Gtk.TreeView):
column_title = "Name"
saved_size = data["cols"][column_title]
column.set_fixed_width(saved_size)
column.set_expand(True)
else:
if ("Name" in column_title):
column.set_fixed_width(800)
if (column_title == "Map"):
column.set_fixed_width(300)
self.append_column(column)
if i != 10:
self.append_column(column)
self.update_first_col(mode.dict["label"])
@ -2330,12 +2353,8 @@ class Grid(Gtk.Grid):
self._version = "%s %s" %(app_name, sys.argv[2])
self.scrollable_treelist = ScrollableTree(is_steam_deck)
if is_steam_deck is True:
self.scrollable_treelist.set_hexpand(False)
self.scrollable_treelist.set_vexpand(True)
else:
self.scrollable_treelist.set_hexpand(True)
self.scrollable_treelist.set_vexpand(True)
self.scrollable_treelist.set_hexpand(False)
self.scrollable_treelist.set_vexpand(True)
self.right_panel = RightPanel(is_steam_deck)
self.sel_panel = ModSelectionPanel()
@ -2352,14 +2371,9 @@ class Grid(Gtk.Grid):
self.bar.add(self.status_right_label)
self.update_right_statusbar()
if is_steam_deck is True:
self.attach(self.scrollable_treelist, 0, 0, 4, 1)
self.attach_next_to(self.bar, self.scrollable_treelist, Gtk.PositionType.BOTTOM, 4, 1)
self.attach_next_to(self.right_panel, self.scrollable_treelist, Gtk.PositionType.RIGHT, 1, 1)
else:
self.attach(self.scrollable_treelist, 0, 0, 7, 5)
self.attach_next_to(self.bar, self.scrollable_treelist, Gtk.PositionType.BOTTOM, 7, 1)
self.attach_next_to(self.right_panel, self.scrollable_treelist, Gtk.PositionType.RIGHT, 1, 1)
self.attach(self.scrollable_treelist, 0, 0, 3, 1)
self.attach_next_to(self.bar, self.scrollable_treelist, Gtk.PositionType.BOTTOM, 3, 1)
self.attach_next_to(self.right_panel, self.scrollable_treelist, Gtk.PositionType.RIGHT, 1, 1)
def update_right_statusbar(self):
config_vals.clear()
@ -2438,16 +2452,50 @@ class App(Gtk.Application):
self.win = OuterWindow(is_steam_deck, is_game_mode)
self.win.set_icon_name("dzgui")
accel = Gtk.AccelGroup()
accel.connect(Gdk.KEY_q, Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE, self._halt_window_subprocess)
self.win.add_accel_group(accel)
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, Gtk.main_quit)
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self._catch_sigint)
Gtk.main()
def _halt_window_subprocess(self, accel_group, window, code, flag):
self.win.halt_proc_and_quit(self, None)
def _catch_sigint(self):
self.win.halt_proc_and_quit(self.win, None)
def _halt_window_subprocess(self, accel_group, window, code, flag):
self.win.halt_proc_and_quit(self.win, None)
def save_res_and_quit(window):
if window.props.is_maximized:
Gtk.main_quit()
return
rect = window.get_size()
def write_json(rect):
data = {"res": { "width": rect.width, "height": rect.height } }
j = json.dumps(data, indent=2)
with open(res_path, "w") as outfile:
outfile.write(j)
logger.info("Wrote initial window size to '%s'" %(res_path))
if os.path.isfile(res_path):
with open(res_path, "r") as infile:
try:
data = json.load(infile)
data["res"]["width"] = rect.width
data["res"]["height"] = rect.height
with open(res_path, "w") as outfile:
outfile.write(json.dumps(data, indent=2))
except json.decoder.JSONDecodeError:
logger.critical("JSON decode error in '%s'" %(res_path))
write_json(rect)
else:
write_json(rect)
Gtk.main_quit()
class ModSelectionPanel(Gtk.Box):
def __init__(self):
@ -2471,7 +2519,6 @@ class ModSelectionPanel(Gtk.Box):
button.connect("clicked", self._on_button_clicked)
self.pack_start(button, False, True, 0)
def initialize(self):
l = len(self.get_children())
last = self.get_children()[l-1]
@ -2483,7 +2530,6 @@ class ModSelectionPanel(Gtk.Box):
case "Unhighlight stale":
i.set_label("Highlight stale")
def _on_button_clicked(self, button):
self.active_button = button
label = button.get_label()
@ -2512,7 +2558,6 @@ class ModSelectionPanel(Gtk.Box):
path = Gtk.TreePath(i)
treeview.get_selection().select_path(path)
def _remove_last_button(self):
children = self.get_children()
l = len(children)
@ -2616,7 +2661,7 @@ class FilterPanel(Gtk.Box):
self.keyword_entry.set_placeholder_text("Filter by keyword")
self.keyword_entry.connect("activate", self._on_keyword_enter)
self.keyword_entry.connect("key-press-event", self._on_esc_pressed)
completion = Gtk.EntryCompletion(inline_completion=True)
completion.set_text_column(0)
completion.set_minimum_key_length(1)
@ -2625,7 +2670,7 @@ class FilterPanel(Gtk.Box):
renderer_text = Gtk.CellRendererText(ellipsize=Pango.EllipsizeMode.END)
self.maps_combo = Gtk.ComboBox.new_with_model_and_entry(map_store)
self.maps_combo.set_entry_text_column(0)
# instantiate maps completer entry
self.maps_entry = self.maps_combo.get_child()
self.maps_entry.set_completion(completion)
@ -2637,12 +2682,20 @@ class FilterPanel(Gtk.Box):
self.maps_combo.connect("changed", self._on_map_changed)
self.maps_combo.connect("key-press-event", self._on_esc_pressed)
self.pack_start(self.filters_label, False, False, True)
self.pack_start(self.keyword_entry, False, False, True)
self.pack_start(self.maps_combo, False, False, True)
self.pack_start(self.filters_label, False, False, 0)
self.pack_start(self.keyword_entry, False, False, 0)
self.pack_start(self.maps_combo, False, False, 0)
button_grid = Gtk.Grid()
row = 1
col = 0
for i, check in enumerate(checks[0:]):
self.pack_start(checks[i], False, False, True)
col = col + 1
if (col > 3):
row = row + 1
col = 1
button_grid.attach(checks[i], col, row, 1, 1)
self.pack_start(button_grid, False, False, 0)
def _on_map_entry_keypress(self, entry, event):
match event.keyval: