mirror of
https://github.com/aclist/dztui.git
synced 2024-12-28 21:32:36 +01:00
commit
bae6a57e1e
6 changed files with 230 additions and 43 deletions
37
CHANGELOG.md
37
CHANGELOG.md
|
@ -1,15 +1,44 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## [5.3.3] 2024-08-27
|
## [5.5.0] 2024-11-10
|
||||||
|
### Added
|
||||||
|
- Support servers running DLC content (fixes Frostline servers)
|
||||||
|
- Expose a toggle setting for whether to launch the application in fullscreen
|
||||||
|
- Text autocompletion in maps search field (partial search)
|
||||||
|
- Add disk space warning to popup dialog when downloading mods
|
||||||
|
### Fixed
|
||||||
|
- Servers in saved servers list would populate context menu with same option when right-clicking in server browser
|
||||||
|
- Enable adding/removing servers to/from My Saved Servers when in Recent Servers context
|
||||||
|
- Prevent maps combobox from duplicating contents
|
||||||
|
- Restore keyboard input to keyword entry field
|
||||||
|
### Changed
|
||||||
|
- Abort fallback query method if DLC is required
|
||||||
|
|
||||||
|
## [5.4.2] 2024-10-05
|
||||||
|
### Fixed
|
||||||
|
- Sanitize third-party API IDs to remove UGC collisions
|
||||||
|
|
||||||
|
## [5.4.1] 2024-09-25
|
||||||
|
### Added
|
||||||
|
- Pre-boot validation check for users with self-compiled version of jq
|
||||||
|
### Fixed
|
||||||
|
- Use fallback logic for modlist queries when user traverses networks
|
||||||
|
- Fix signal handling control flow for checkbox toggles
|
||||||
|
- When reloading the server browser, the map combobox selection would revert to the last selected map instead of All Maps
|
||||||
|
- Server filter toggle signals were accessible from the main menu when switching between menu contexts
|
||||||
|
- Global cooldown dialog could sometimes block filter toggles after cooldown reset
|
||||||
|
- Normalized minor version number due to a previous clerical error
|
||||||
|
|
||||||
|
## [5.4.0] 2024-08-27
|
||||||
### Added
|
### Added
|
||||||
- Scan local area network for DayZ servers
|
- Scan local area network for DayZ servers
|
||||||
- Freedesktop application icons for system taskbar, tray, and other dialogs
|
- Freedesktop application icons for system taskbar, tray, and other dialogs
|
||||||
- Emit CPU model name when exporting system debug log
|
- Emit CPU model name when exporting system debug log
|
||||||
### Fixed
|
### Fixed
|
||||||
- Detect Steam Deck OLED APU variant during initial setup
|
|
||||||
- Errors being printed to the console when Exit button was explicitly clicked
|
- Errors being printed to the console when Exit button was explicitly clicked
|
||||||
- Test if DayZ library location was moved internally on Steam by user
|
- Detect Steam Deck OLED APU variant during initial setup
|
||||||
- Encapsulate player names correctly to support whitespace
|
- Encapsulate player names correctly so that names with whitespace in them are supported
|
||||||
|
- Test if DayZ directory is empty at startup, implying that the game was moved to a new library collection
|
||||||
- Report WM_CLASS name to the window manager
|
- Report WM_CLASS name to the window manager
|
||||||
|
|
||||||
## [5.3.2] 2024-07-02
|
## [5.3.2] 2024-07-02
|
||||||
|
|
|
@ -33,6 +33,10 @@ All dependencies are installed out of the box on Steam Deck.
|
||||||
- `wmctrl` or `xdotool`
|
- `wmctrl` or `xdotool`
|
||||||
- `PyGObject` (`python-gobject`)
|
- `PyGObject` (`python-gobject`)
|
||||||
|
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
If you are using a self-compiled version of jq (e.g. gentoo), it must be configured with support for oniguruma (this is the default setting on most distributions).
|
||||||
|
|
||||||
=== Preparation
|
=== Preparation
|
||||||
==== Step 1: Download DZGUI and make it executable
|
==== Step 1: Download DZGUI and make it executable
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,10 @@ All dependencies are installed out of the box on Steam Deck.
|
||||||
- `wmctrl` or `xdotool`
|
- `wmctrl` or `xdotool`
|
||||||
- `PyGObject` (`python-gobject`)
|
- `PyGObject` (`python-gobject`)
|
||||||
|
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
If you are using a self-compiled version of jq (e.g. gentoo), it must be configured with support for oniguruma (this is the default setting on most distributions).
|
||||||
|
|
||||||
=== Preparation
|
=== Preparation
|
||||||
==== Step 1: Download DZGUI and make it executable
|
==== Step 1: Download DZGUI and make it executable
|
||||||
|
|
||||||
|
|
19
dzgui.sh
19
dzgui.sh
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
version=5.3.0
|
version=5.5.0
|
||||||
|
|
||||||
#CONSTANTS
|
#CONSTANTS
|
||||||
aid=221100
|
aid=221100
|
||||||
|
@ -193,6 +193,9 @@ debug="$debug"
|
||||||
#Toggle stable/testing branch
|
#Toggle stable/testing branch
|
||||||
branch="$branch"
|
branch="$branch"
|
||||||
|
|
||||||
|
#Start in fullscreen
|
||||||
|
fullscreen="$fullscreen"
|
||||||
|
|
||||||
#Steam API key
|
#Steam API key
|
||||||
steam_api="$steam_api"
|
steam_api="$steam_api"
|
||||||
|
|
||||||
|
@ -220,6 +223,10 @@ depcheck(){
|
||||||
raise_error_and_quit "$msg"
|
raise_error_and_quit "$msg"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
local jqmsg="jq must be compiled with support for oniguruma"
|
||||||
|
local jqtest
|
||||||
|
jqtest=$(echo '{"test": "foo"}' | jq '.test | test("^foo$")')
|
||||||
|
[[ $? -ne 0 ]] && raise_error_and_quit "$jqmsg"
|
||||||
logger INFO "Initial dependencies satisfied"
|
logger INFO "Initial dependencies satisfied"
|
||||||
}
|
}
|
||||||
check_pyver(){
|
check_pyver(){
|
||||||
|
@ -521,14 +528,14 @@ fetch_a2s(){
|
||||||
logger INFO "Updated A2S helper to sha '$sha'"
|
logger INFO "Updated A2S helper to sha '$sha'"
|
||||||
}
|
}
|
||||||
fetch_dzq(){
|
fetch_dzq(){
|
||||||
local sum="232f42b98a3b50a0dd6e73fee55521b2"
|
local sum="9caed1445c45832f4af87736ba3f9637"
|
||||||
local file="$helpers_path/a2s/dayzquery.py"
|
local file="$helpers_path/a2s/dayzquery.py"
|
||||||
if [[ -f $file ]] && [[ $(get_hash "$file") == $sum ]]; then
|
if [[ -f $file ]] && [[ $(get_hash "$file") == $sum ]]; then
|
||||||
logger INFO "DZQ is current"
|
logger INFO "DZQ is current"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
local sha=ccc4f71b48610a1885706c9d92638dbd8ca012a5
|
local sha=3088bbfb147b77bc7b6a9425581b439889ff3f7f
|
||||||
local author="yepoleb"
|
local author="aclist"
|
||||||
local repo="dayzquery"
|
local repo="dayzquery"
|
||||||
local url="https://raw.githubusercontent.com/$author/$repo/$sha/dayzquery.py"
|
local url="https://raw.githubusercontent.com/$author/$repo/$sha/dayzquery.py"
|
||||||
curl -Ls "$url" > "$file"
|
curl -Ls "$url" > "$file"
|
||||||
|
@ -562,10 +569,10 @@ fetch_helpers_by_sum(){
|
||||||
[[ -f "$config_file" ]] && source "$config_file"
|
[[ -f "$config_file" ]] && source "$config_file"
|
||||||
declare -A sums
|
declare -A sums
|
||||||
sums=(
|
sums=(
|
||||||
["ui.py"]="819a30c43644817a4f4a009f3df52b77"
|
["ui.py"]="dd7aa34df1d374739127cca3033a3f67"
|
||||||
["query_v2.py"]="55d339ba02512ac69de288eb3be41067"
|
["query_v2.py"]="55d339ba02512ac69de288eb3be41067"
|
||||||
["vdf2json.py"]="2f49f6f5d3af919bebaab2e9c220f397"
|
["vdf2json.py"]="2f49f6f5d3af919bebaab2e9c220f397"
|
||||||
["funcs"]="e1998f02f17776ccf2108fe5e9396d75"
|
["funcs"]="d8ae2662fbc3c62bdb5a51dec1935705"
|
||||||
["lan"]="c62e84ddd1457b71a85ad21da662b9af"
|
["lan"]="c62e84ddd1457b71a85ad21da662b9af"
|
||||||
)
|
)
|
||||||
local author="aclist"
|
local author="aclist"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
version=5.3.0
|
version=5.5.0
|
||||||
|
|
||||||
#CONSTANTS
|
#CONSTANTS
|
||||||
aid=221100
|
aid=221100
|
||||||
|
@ -280,12 +280,28 @@ initialize_remote_servers(){
|
||||||
res=$(get_remote_servers)
|
res=$(get_remote_servers)
|
||||||
parse_server_json "$res" >> "$file"
|
parse_server_json "$res" >> "$file"
|
||||||
}
|
}
|
||||||
|
is_dlc(){
|
||||||
|
local dlc
|
||||||
|
local ip="$1"
|
||||||
|
local gport="$2"
|
||||||
|
local res="$(curl -Ls "https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\gameaddr\\${ip}:${gport}\appid\221100&key=$steam_api")"
|
||||||
|
dlc=$(<<< "$res" jq '.response.servers[].gametype|contains("isDLC")')
|
||||||
|
printf "%s\n" "$dlc"
|
||||||
|
}
|
||||||
a2s(){
|
a2s(){
|
||||||
local ip="$1"
|
local ip="$1"
|
||||||
local qport="$2"
|
local qport="$2"
|
||||||
local mode="$3"
|
local mode="$3"
|
||||||
logger INFO "Querying '$ip:$qport' with mode '$mode'"
|
logger INFO "Querying '$ip:$qport' with mode '$mode'"
|
||||||
python3 "$query_helper" "$ip" "$qport" "$mode"
|
local res
|
||||||
|
res=$(python3 "$query_helper" "$ip" "$qport" "$mode")
|
||||||
|
if [[ $? -eq 1 ]]; then
|
||||||
|
res=$(try_fallback "$ip" "$qport" "$mode")
|
||||||
|
if [[ $? -eq 1 ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
printf "%s\n" "$res"
|
||||||
}
|
}
|
||||||
is_in_favs(){
|
is_in_favs(){
|
||||||
shift
|
shift
|
||||||
|
@ -394,6 +410,7 @@ query_config(){
|
||||||
"name"
|
"name"
|
||||||
"fav_label"
|
"fav_label"
|
||||||
"preferred_client"
|
"preferred_client"
|
||||||
|
"fullscreen"
|
||||||
)
|
)
|
||||||
if [[ -n $key ]]; then
|
if [[ -n $key ]]; then
|
||||||
if [[ -n ${!key} ]]; then
|
if [[ -n ${!key} ]]; then
|
||||||
|
@ -698,6 +715,9 @@ debug="$debug"
|
||||||
#Toggle stable/testing branch
|
#Toggle stable/testing branch
|
||||||
branch="$branch"
|
branch="$branch"
|
||||||
|
|
||||||
|
#Start in fullscreen
|
||||||
|
fullscreen="$fullscreen"
|
||||||
|
|
||||||
#Steam API key
|
#Steam API key
|
||||||
steam_api="$steam_api"
|
steam_api="$steam_api"
|
||||||
|
|
||||||
|
@ -791,6 +811,14 @@ toggle(){
|
||||||
else
|
else
|
||||||
preferred_client="steam"
|
preferred_client="steam"
|
||||||
fi
|
fi
|
||||||
|
;;
|
||||||
|
Toggle[[:space:]]DZGUI[[:space:]]fullscreen[[:space:]]boot)
|
||||||
|
if [[ $fullscreen == "true" ]]; then
|
||||||
|
fullscreen="false"
|
||||||
|
else
|
||||||
|
fullscreen="true"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
update_config
|
update_config
|
||||||
return 90
|
return 90
|
||||||
|
@ -1011,7 +1039,11 @@ query_defunct(){
|
||||||
-H "Content-Type:application/x-www-form-urlencoded"\
|
-H "Content-Type:application/x-www-form-urlencoded"\
|
||||||
-d "$(payload)" 'https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1/?format=json'
|
-d "$(payload)" 'https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1/?format=json'
|
||||||
}
|
}
|
||||||
local result=$(post | jq -r '.[].publishedfiledetails[] | select(.result==1) | "\(.file_size) \(.publishedfileid)"')
|
local result=$(post | jq -r '
|
||||||
|
.[].publishedfiledetails[]
|
||||||
|
| select(.result==1)
|
||||||
|
| select(.filename|contains("screenshot")|not)
|
||||||
|
| "\(.file_size) \(.publishedfileid)"')
|
||||||
<<< "$result" awk '{print $2}'
|
<<< "$result" awk '{print $2}'
|
||||||
}
|
}
|
||||||
encode(){
|
encode(){
|
||||||
|
@ -1064,6 +1096,48 @@ update_symlinks(){
|
||||||
legacy_symlinks
|
legacy_symlinks
|
||||||
symlinks
|
symlinks
|
||||||
}
|
}
|
||||||
|
try_fallback(){
|
||||||
|
local ip="$1"
|
||||||
|
local qport="$2"
|
||||||
|
local mode="$3"
|
||||||
|
if [[ $mode != "rules" ]] && [[ $mode != "names" ]]; then
|
||||||
|
logger WARN "Fallback query API called with an invalid mode: '$mode'"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
[[ -z $api_key ]] && return 1
|
||||||
|
logger INFO "Using fallback query API on '$ip:$qport' with mode: '$mode'"
|
||||||
|
local res=$(curl -s "$bm_api" -H "Authorization: Bearer "$api_key"" \
|
||||||
|
-G -d "filter[game]=$game" \
|
||||||
|
-d "filter[search]=%22${ip}:${qport}%22")
|
||||||
|
[[ -z $res ]] && return 1
|
||||||
|
local len=$(<<< "$res" jq '.data|length')
|
||||||
|
[[ $len -eq 0 ]] && return 1
|
||||||
|
|
||||||
|
# cull defunct entries
|
||||||
|
local record=$(<<< "$res" jq -r '
|
||||||
|
.data[].attributes
|
||||||
|
|select(.status != "removed")')
|
||||||
|
|
||||||
|
# reverse lookup gport
|
||||||
|
local dlc
|
||||||
|
local gport=$(<<< "$record" jq -r '.port')
|
||||||
|
dlc=$(is_dlc "$ip" "$gport")
|
||||||
|
if [[ $dlc == "true" ]]; then
|
||||||
|
logger FAIL "Fallback query API called on DLC-enabled server"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$mode" in
|
||||||
|
"rules")
|
||||||
|
<<< "$record" jq '.details.modIds[]'
|
||||||
|
;;
|
||||||
|
"names")
|
||||||
|
<<< "$record" jq '
|
||||||
|
.details|[.modNames, .modIds] as [$n, $i]
|
||||||
|
| {names: $n, ids: $i}'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
try_connect(){
|
try_connect(){
|
||||||
local record="$1"
|
local record="$1"
|
||||||
local ip=$(<<< $record awk -F: '{print $1}')
|
local ip=$(<<< $record awk -F: '{print $1}')
|
||||||
|
@ -1141,6 +1215,7 @@ focus_beta_client(){
|
||||||
$steam_cmd steam://open/console 2>/dev/null 1>&2
|
$steam_cmd steam://open/console 2>/dev/null 1>&2
|
||||||
}
|
}
|
||||||
auto_mod_install(){
|
auto_mod_install(){
|
||||||
|
# currently unused, merged with manual method
|
||||||
local ip="$1"
|
local ip="$1"
|
||||||
local gameport="$2"
|
local gameport="$2"
|
||||||
local diff="$3"
|
local diff="$3"
|
||||||
|
@ -1362,7 +1437,7 @@ manual_mod_install(){
|
||||||
|
|
||||||
if [[ $mode == "auto" ]] || [[ $mode == "force" ]]; then
|
if [[ $mode == "auto" ]] || [[ $mode == "force" ]]; then
|
||||||
$steam_cmd "steam://url/CommunityFilePage/${stage_mods[$i]}+workshop_download_item $aid ${stage_mods[$i]}"
|
$steam_cmd "steam://url/CommunityFilePage/${stage_mods[$i]}+workshop_download_item $aid ${stage_mods[$i]}"
|
||||||
echo "# Opening workshop page for ${stage_mods[$i]}"
|
echo "# Opening workshop page for ${stage_mods[$i]}. If you see no progress, you may be out of disk space."
|
||||||
else
|
else
|
||||||
$steam_cmd "steam://url/CommunityFilePage/${stage_mods[$i]}"
|
$steam_cmd "steam://url/CommunityFilePage/${stage_mods[$i]}"
|
||||||
echo "# Opening workshop page for ${stage_mods[$i]}. If you see no progress after subscribing, try unsubscribing and resubscribing again until the download commences."
|
echo "# Opening workshop page for ${stage_mods[$i]}. If you see no progress after subscribing, try unsubscribing and resubscribing again until the download commences."
|
||||||
|
|
110
helpers/ui.py
110
helpers/ui.py
|
@ -19,7 +19,7 @@ gi.require_version("Gtk", "3.0")
|
||||||
from gi.repository import Gtk, GLib, Gdk, GObject, Pango
|
from gi.repository import Gtk, GLib, Gdk, GObject, Pango
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
# 5.3.0
|
# 5.5.0
|
||||||
app_name = "DZGUI"
|
app_name = "DZGUI"
|
||||||
|
|
||||||
start_time = 0
|
start_time = 0
|
||||||
|
@ -105,6 +105,7 @@ options = [
|
||||||
("Toggle release branch",),
|
("Toggle release branch",),
|
||||||
("Toggle mod install mode",),
|
("Toggle mod install mode",),
|
||||||
("Toggle Steam/Flatpak",),
|
("Toggle Steam/Flatpak",),
|
||||||
|
("Toggle DZGUI fullscreen boot",),
|
||||||
("Change player name",),
|
("Change player name",),
|
||||||
("Change Steam API key",),
|
("Change Steam API key",),
|
||||||
("Change Battlemetrics API key",),
|
("Change Battlemetrics API key",),
|
||||||
|
@ -153,6 +154,7 @@ status_tooltip = {
|
||||||
"Toggle release branch": "Switch between stable and testing branches",
|
"Toggle release branch": "Switch between stable and testing branches",
|
||||||
"Toggle mod install mode": "Switch between manual and auto mod installation",
|
"Toggle mod install mode": "Switch between manual and auto mod installation",
|
||||||
"Toggle Steam/Flatpak": "Switch the preferred client to use for launching DayZ",
|
"Toggle Steam/Flatpak": "Switch the preferred client to use for launching DayZ",
|
||||||
|
"Toggle DZGUI fullscreen boot": "Whether to start DZGUI as a maximized window (desktop only)",
|
||||||
"Change player name": "Update your in-game name (required by some servers)",
|
"Change player name": "Update your in-game name (required by some servers)",
|
||||||
"Change Steam API key": "Can be used if you revoked an old API key",
|
"Change Steam API key": "Can be used if you revoked an old API key",
|
||||||
"Change Battlemetrics API key": "Can be used if you revoked an old API key",
|
"Change Battlemetrics API key": "Can be used if you revoked an old API key",
|
||||||
|
@ -348,7 +350,12 @@ def process_tree_option(input, treeview):
|
||||||
logger.info("Parsing tree option '%s' for the context '%s'" %(command, context))
|
logger.info("Parsing tree option '%s' for the context '%s'" %(command, context))
|
||||||
|
|
||||||
transient_parent = treeview.get_outer_window()
|
transient_parent = treeview.get_outer_window()
|
||||||
toggle_contexts = ["Toggle mod install mode", "Toggle release branch", "Toggle Steam/Flatpak"]
|
toggle_contexts = [
|
||||||
|
"Toggle mod install mode",
|
||||||
|
"Toggle release branch",
|
||||||
|
"Toggle Steam/Flatpak",
|
||||||
|
"Toggle DZGUI fullscreen boot"
|
||||||
|
]
|
||||||
|
|
||||||
def call_on_thread(bool, subproc, msg, args):
|
def call_on_thread(bool, subproc, msg, args):
|
||||||
def _background(subproc, args, dialog):
|
def _background(subproc, args, dialog):
|
||||||
|
@ -374,7 +381,6 @@ def process_tree_option(input, treeview):
|
||||||
msg = out[-1]
|
msg = out[-1]
|
||||||
process_shell_return_code(transient_parent, msg, rc, input)
|
process_shell_return_code(transient_parent, msg, rc, input)
|
||||||
|
|
||||||
|
|
||||||
match context:
|
match context:
|
||||||
case "Help":
|
case "Help":
|
||||||
if command == "View changelog":
|
if command == "View changelog":
|
||||||
|
@ -465,6 +471,7 @@ class OuterWindow(Gtk.Window):
|
||||||
if is_game_mode is True:
|
if is_game_mode is True:
|
||||||
self.fullscreen()
|
self.fullscreen()
|
||||||
else:
|
else:
|
||||||
|
if query_config(None, "fullscreen")[0] == "true":
|
||||||
self.maximize()
|
self.maximize()
|
||||||
|
|
||||||
# Hide FilterPanel on main menu
|
# Hide FilterPanel on main menu
|
||||||
|
@ -574,6 +581,7 @@ class ButtonBox(Gtk.Box):
|
||||||
column = Gtk.TreeViewColumn(column_title, renderer, text=i)
|
column = Gtk.TreeViewColumn(column_title, renderer, text=i)
|
||||||
treeview.append_column(column)
|
treeview.append_column(column)
|
||||||
self._populate(context)
|
self._populate(context)
|
||||||
|
toggle_signal(treeview, treeview, '_on_keypress', False)
|
||||||
treeview.set_model(row_store)
|
treeview.set_model(row_store)
|
||||||
treeview.grab_focus()
|
treeview.grab_focus()
|
||||||
|
|
||||||
|
@ -758,7 +766,7 @@ class TreeView(Gtk.TreeView):
|
||||||
mod_context_items = ["Open in Steam Workshop", "Delete mod"]
|
mod_context_items = ["Open in Steam Workshop", "Delete mod"]
|
||||||
subcontext_items = {"Server browser": ["Add to my servers", "Copy IP to clipboard", "Show server-side mods", "Refresh player count"],
|
subcontext_items = {"Server browser": ["Add to my servers", "Copy IP to clipboard", "Show server-side mods", "Refresh player count"],
|
||||||
"My saved servers": ["Remove from my servers", "Copy IP to clipboard", "Show server-side mods", "Refresh player count"],
|
"My saved servers": ["Remove from my servers", "Copy IP to clipboard", "Show server-side mods", "Refresh player count"],
|
||||||
"Recent servers": ["Remove from history", "Copy IP to clipboard", "Show server-side mods", "Refresh player count"],
|
"Recent servers": ["Add to my servers", "Remove from history", "Copy IP to clipboard", "Show server-side mods", "Refresh player count"],
|
||||||
}
|
}
|
||||||
# submenu hierarchy https://stackoverflow.com/questions/52847909/how-to-add-a-sub-menu-to-a-gtk-menu
|
# submenu hierarchy https://stackoverflow.com/questions/52847909/how-to-add-a-sub-menu-to-a-gtk-menu
|
||||||
if context == "Mod":
|
if context == "Mod":
|
||||||
|
@ -771,7 +779,8 @@ class TreeView(Gtk.TreeView):
|
||||||
return
|
return
|
||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
if subcontext == "Server browser" and item == "Add to my servers":
|
if subcontext == "Server browser" or "Recent servers":
|
||||||
|
if item == "Add to my servers":
|
||||||
record = "%s:%s" %(self.get_column_at_index(7), self.get_column_at_index(8))
|
record = "%s:%s" %(self.get_column_at_index(7), self.get_column_at_index(8))
|
||||||
proc = call_out(widget, "is_in_favs", record)
|
proc = call_out(widget, "is_in_favs", record)
|
||||||
if proc.returncode == 0:
|
if proc.returncode == 0:
|
||||||
|
@ -894,8 +903,7 @@ class TreeView(Gtk.TreeView):
|
||||||
case Gdk.KEY_m:
|
case Gdk.KEY_m:
|
||||||
if self.get_first_col() == "Mod":
|
if self.get_first_col() == "Mod":
|
||||||
return
|
return
|
||||||
grid.right_panel.filters_vbox.maps_combo.grab_focus()
|
grid.right_panel.filters_vbox.maps_entry.grab_focus()
|
||||||
grid.right_panel.filters_vbox.maps_combo.popup()
|
|
||||||
case _:
|
case _:
|
||||||
return False
|
return False
|
||||||
elif keyname.isnumeric() and int(keyname) > 0:
|
elif keyname.isnumeric() and int(keyname) > 0:
|
||||||
|
@ -1033,7 +1041,6 @@ class TreeView(Gtk.TreeView):
|
||||||
# Local server lists may have different filter toggles from remote list
|
# Local server lists may have different filter toggles from remote list
|
||||||
# FIXME: tree selection updates twice here. attach signal later
|
# FIXME: tree selection updates twice here. attach signal later
|
||||||
toggle_signal(self, self.selected_row, '_on_tree_selection_changed', False)
|
toggle_signal(self, self.selected_row, '_on_tree_selection_changed', False)
|
||||||
# toggle_signal(self, self.selected_row, '_on_check_toggled', False)
|
|
||||||
for column in self.get_columns():
|
for column in self.get_columns():
|
||||||
self.remove_column(column)
|
self.remove_column(column)
|
||||||
row_store.clear()
|
row_store.clear()
|
||||||
|
@ -1071,6 +1078,10 @@ class TreeView(Gtk.TreeView):
|
||||||
self.update_first_col(mode)
|
self.update_first_col(mode)
|
||||||
transient_parent = self.get_outer_window()
|
transient_parent = self.get_outer_window()
|
||||||
|
|
||||||
|
# Reset map selection
|
||||||
|
selected_map.clear()
|
||||||
|
selected_map.append("Map=All maps")
|
||||||
|
|
||||||
for check in checks:
|
for check in checks:
|
||||||
toggle_signal(self.get_outer_grid().right_panel.filters_vbox, check, '_on_check_toggle', True)
|
toggle_signal(self.get_outer_grid().right_panel.filters_vbox, check, '_on_check_toggle', True)
|
||||||
toggle_signal(self, self, '_on_keypress', True)
|
toggle_signal(self, self, '_on_keypress', True)
|
||||||
|
@ -1190,17 +1201,18 @@ class TreeView(Gtk.TreeView):
|
||||||
valid_contexts = ["Server browser", "My saved servers", "Recent servers", "Scan LAN servers"]
|
valid_contexts = ["Server browser", "My saved servers", "Recent servers", "Scan LAN servers"]
|
||||||
if chosen_row in valid_contexts:
|
if chosen_row in valid_contexts:
|
||||||
# server contexts share the same model type
|
# server contexts share the same model type
|
||||||
for check in checks:
|
|
||||||
toggle_signal(filters_vbox, check, '_on_check_toggle', False)
|
|
||||||
|
|
||||||
if chosen_row == "Server browser":
|
if chosen_row == "Server browser":
|
||||||
reinit_checks()
|
|
||||||
cooldown = call_out(self, "test_cooldown", "", "")
|
cooldown = call_out(self, "test_cooldown", "", "")
|
||||||
if cooldown.returncode == 1:
|
if cooldown.returncode == 1:
|
||||||
spawn_dialog(self.get_outer_window(), cooldown.stdout, "NOTIFY")
|
spawn_dialog(self.get_outer_window(), cooldown.stdout, "NOTIFY")
|
||||||
return 1
|
return 1
|
||||||
|
for check in checks:
|
||||||
|
toggle_signal(filters_vbox, check, '_on_check_toggle', False)
|
||||||
|
reinit_checks()
|
||||||
else:
|
else:
|
||||||
for check in checks:
|
for check in checks:
|
||||||
|
toggle_signal(filters_vbox, check, '_on_check_toggle', False)
|
||||||
if check.get_label() not in toggled_checks:
|
if check.get_label() not in toggled_checks:
|
||||||
toggled_checks.append(check.get_label())
|
toggled_checks.append(check.get_label())
|
||||||
check.set_active(True)
|
check.set_active(True)
|
||||||
|
@ -1233,7 +1245,8 @@ def format_metadata(row_sel):
|
||||||
"auto_install": config_vals[2],
|
"auto_install": config_vals[2],
|
||||||
"name": config_vals[3],
|
"name": config_vals[3],
|
||||||
"fav_label": config_vals[4],
|
"fav_label": config_vals[4],
|
||||||
"preferred_client": config_vals[5]
|
"preferred_client": config_vals[5],
|
||||||
|
"fullscreen": config_vals[6]
|
||||||
}
|
}
|
||||||
match row_sel:
|
match row_sel:
|
||||||
case "Quick-connect to favorite server" | "Change favorite server":
|
case "Quick-connect to favorite server" | "Change favorite server":
|
||||||
|
@ -1253,6 +1266,10 @@ def format_metadata(row_sel):
|
||||||
val = "branch"
|
val = "branch"
|
||||||
case "Toggle Steam/Flatpak":
|
case "Toggle Steam/Flatpak":
|
||||||
val = "preferred_client"
|
val = "preferred_client"
|
||||||
|
case "Toggle DZGUI fullscreen boot":
|
||||||
|
default = "false"
|
||||||
|
alt = "true"
|
||||||
|
val = "fullscreen"
|
||||||
case _:
|
case _:
|
||||||
return prefix
|
return prefix
|
||||||
|
|
||||||
|
@ -1561,7 +1578,7 @@ def KeysDialog(parent, text, mode):
|
||||||
Enter/Space/Double click: connect to server
|
Enter/Space/Double click: connect to server
|
||||||
Right-click on row/Ctrl-l: displays additional context menus
|
Right-click on row/Ctrl-l: displays additional context menus
|
||||||
Ctrl-f: jump to keyword field
|
Ctrl-f: jump to keyword field
|
||||||
Ctrl-m: jump to maps dropdown
|
Ctrl-m: jump to maps field
|
||||||
Ctrl-d: toggle dry run (debug) mode
|
Ctrl-d: toggle dry run (debug) mode
|
||||||
Ctrl-r: refresh player count for active row
|
Ctrl-r: refresh player count for active row
|
||||||
1-9: toggle filter ON/OFF
|
1-9: toggle filter ON/OFF
|
||||||
|
@ -1634,7 +1651,7 @@ class ModDialog(GenericDialog):
|
||||||
def _load():
|
def _load():
|
||||||
dialog.destroy()
|
dialog.destroy()
|
||||||
if data.returncode == 1:
|
if data.returncode == 1:
|
||||||
spawn_dialog(parent, "Server has no mods installed", "NOTIFY")
|
spawn_dialog(parent, "Server has no mods installed or is unsupported in this mode", "NOTIFY")
|
||||||
return
|
return
|
||||||
self.show_all()
|
self.show_all()
|
||||||
self.set_markup("Modlist (%s mods)" %(mod_count))
|
self.set_markup("Modlist (%s mods)" %(mod_count))
|
||||||
|
@ -1854,14 +1871,26 @@ class FilterPanel(Gtk.Box):
|
||||||
self.keyword_entry.connect("activate", self._on_keyword_enter)
|
self.keyword_entry.connect("activate", self._on_keyword_enter)
|
||||||
self.keyword_entry.connect("key-press-event", self._on_esc_pressed)
|
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)
|
||||||
|
completion.connect("match_selected", self._on_completer_match)
|
||||||
|
|
||||||
renderer_text = Gtk.CellRendererText(ellipsize=Pango.EllipsizeMode.END)
|
renderer_text = Gtk.CellRendererText(ellipsize=Pango.EllipsizeMode.END)
|
||||||
self.maps_combo = Gtk.ComboBox.new_with_model(map_store)
|
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)
|
||||||
|
self.maps_entry.set_placeholder_text("Filter by map")
|
||||||
|
self.maps_entry.connect("changed", self._on_map_completion, True)
|
||||||
|
self.maps_entry.connect("key-press-event", self._on_map_entry_keypress)
|
||||||
|
|
||||||
self.maps_combo.pack_start(renderer_text, True)
|
self.maps_combo.pack_start(renderer_text, True)
|
||||||
self.maps_combo.add_attribute(renderer_text, "text", 0)
|
|
||||||
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_esc_pressed)
|
self.maps_combo.connect("key-press-event", self._on_esc_pressed)
|
||||||
|
|
||||||
|
|
||||||
self.pack_start(self.filters_label, False, False, True)
|
self.pack_start(self.filters_label, False, False, True)
|
||||||
self.pack_start(self.keyword_entry, 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.maps_combo, False, False, True)
|
||||||
|
@ -1869,6 +1898,37 @@ class FilterPanel(Gtk.Box):
|
||||||
for i, check in enumerate(checks[0:]):
|
for i, check in enumerate(checks[0:]):
|
||||||
self.pack_start(checks[i], False, False, True)
|
self.pack_start(checks[i], False, False, True)
|
||||||
|
|
||||||
|
def _on_map_entry_keypress(self, entry, event):
|
||||||
|
match event.keyval:
|
||||||
|
case Gdk.KEY_Return:
|
||||||
|
text = entry.get_text()
|
||||||
|
if text is None:
|
||||||
|
return
|
||||||
|
# if entry is exact match for value in liststore,
|
||||||
|
# trigger map change function
|
||||||
|
for i in enumerate(map_store):
|
||||||
|
if text == i[1][0]:
|
||||||
|
self.maps_combo.set_active(i[0])
|
||||||
|
self._on_map_changed(self.maps_combo)
|
||||||
|
case Gdk.KEY_Escape:
|
||||||
|
GLib.idle_add(self.restore_focus_to_treeview)
|
||||||
|
# TODO: this is a workaround for widget.grab_remove()
|
||||||
|
# set cursor position to SOL when unfocusing
|
||||||
|
text = self.maps_entry.get_text()
|
||||||
|
self.maps_entry.set_position(len(text))
|
||||||
|
case _:
|
||||||
|
return
|
||||||
|
|
||||||
|
def _on_completer_match(self, completion, model, iter):
|
||||||
|
self.maps_combo.set_active_iter(iter)
|
||||||
|
|
||||||
|
def _on_map_completion(self, entry, editable):
|
||||||
|
text = entry.get_text()
|
||||||
|
completion = entry.get_completion()
|
||||||
|
|
||||||
|
if len(text) >= completion.get_minimum_key_length():
|
||||||
|
completion.set_model(map_store)
|
||||||
|
self._on_map_changed(self.maps_combo)
|
||||||
|
|
||||||
def grab_keyword_focus(self):
|
def grab_keyword_focus(self):
|
||||||
self.keyword_entry.grab_focus()
|
self.keyword_entry.grab_focus()
|
||||||
|
@ -1879,9 +1939,11 @@ class FilterPanel(Gtk.Box):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _on_esc_pressed(self, entry, event):
|
def _on_esc_pressed(self, entry, event):
|
||||||
keyname = Gdk.keyval_name(event.keyval)
|
match event.keyval:
|
||||||
if keyname == "Escape":
|
case Gdk.KEY_Escape:
|
||||||
GLib.idle_add(self.restore_focus_to_treeview)
|
GLib.idle_add(self.restore_focus_to_treeview)
|
||||||
|
case _:
|
||||||
|
return False
|
||||||
|
|
||||||
def get_outer_grid(self):
|
def get_outer_grid(self):
|
||||||
panel = self.get_parent()
|
panel = self.get_parent()
|
||||||
|
@ -1947,13 +2009,19 @@ class FilterPanel(Gtk.Box):
|
||||||
|
|
||||||
tree_iter = combo.get_active_iter()
|
tree_iter = combo.get_active_iter()
|
||||||
if tree_iter is not None:
|
if tree_iter is not None:
|
||||||
selected_map.clear()
|
# take no action if completer query is same as current map sel
|
||||||
|
old_sel = selected_map[0].split("Map=")[1]
|
||||||
model = combo.get_model()
|
model = combo.get_model()
|
||||||
selection = model[tree_iter][0]
|
selection = model[tree_iter][0]
|
||||||
|
if selection == old_sel:
|
||||||
|
return
|
||||||
|
|
||||||
|
selected_map.clear()
|
||||||
|
if selection is not None:
|
||||||
selected_map.append("Map=" + selection)
|
selected_map.append("Map=" + selection)
|
||||||
logger.info("User selected map '%s'" %(selection))
|
logger.info("User selected map '%s'" %(selection))
|
||||||
filter_servers(transient_parent, self, treeview, context)
|
filter_servers(transient_parent, self, treeview, context)
|
||||||
|
self.maps_entry.set_text(selection)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue