mirror of
https://github.com/aclist/dztui.git
synced 2026-08-27 02:07:18 +02:00
fix: update active maps combo on tab change
This commit is contained in:
parent
4d0bb2f4f8
commit
c3e2f6e4b4
@ -361,14 +361,14 @@ def details(ip: str, qport: int) -> Details:
|
||||
return Details(rows, description, True)
|
||||
|
||||
|
||||
def ping(iteration: int, row: list) -> Ping:
|
||||
addr = row[7]
|
||||
qport = row[8]
|
||||
def ping(iteration: int, addr: list, qport: int, ping: int) -> Ping:
|
||||
# addr = row[7]
|
||||
# qport = row[8]
|
||||
|
||||
res = None
|
||||
|
||||
if row[9] != 9999:
|
||||
return Ping(addr, iteration, row[9])
|
||||
if ping != 9999:
|
||||
return Ping(addr, iteration, ping)
|
||||
|
||||
try:
|
||||
ip = addr.split(":")[0]
|
||||
|
||||
@ -115,9 +115,12 @@ class Controller(GObject.GObject):
|
||||
self.emitter = Emitter()
|
||||
self.emitter.connect("map_selection_changed", self._on_map_selection_changed)
|
||||
self.emitter.connect("check_toggled", self._on_check_toggled)
|
||||
self.emitter.connect("servers_loaded", self._on_servers_loaded)
|
||||
self.emitter.connect("servers_loaded_init", self._on_servers_loaded_init)
|
||||
|
||||
# NOTE: suppress requests until entire UI is loaded
|
||||
self.loaded = False
|
||||
self.pending_jobs = 1
|
||||
|
||||
def get_emitter(self) -> Emitter:
|
||||
return self.emitter
|
||||
@ -131,7 +134,7 @@ class Controller(GObject.GObject):
|
||||
GLib.idle_add(self._destroy_on_idle)
|
||||
|
||||
self = args[0]
|
||||
self.wait_dialog = WaitDialog(self, dialog_str, True)
|
||||
self.wait_dialog = WaitDialog(self, dialog_str, jobs=self.pending_jobs)
|
||||
self.wait_dialog.show_all()
|
||||
thread = threading.Thread(target=callback)
|
||||
thread.start()
|
||||
@ -388,48 +391,29 @@ class Controller(GObject.GObject):
|
||||
params = Servers.params
|
||||
serv = []
|
||||
i = 0
|
||||
total = 10
|
||||
with ThreadPoolExecutor() as executor:
|
||||
futures = [executor.submit(job, key, APPID_DAYZ, param) for param in params]
|
||||
# wait(futures)
|
||||
for future in as_completed(futures):
|
||||
try:
|
||||
i += 1
|
||||
GLib.idle_add(lambda: self.wait_dialog.prog.set_fraction(i / total))
|
||||
GLib.idle_add(lambda: self.wait_dialog.increment())
|
||||
res = future.result(timeout=3)
|
||||
if res.status != 200 or not res.parsed:
|
||||
# TODO: pop warning dialog, create enum around various failure states
|
||||
# set failure type here
|
||||
self.cleanup_func = CleanupFunc(self.cleanup_on_failure)
|
||||
return
|
||||
j = res.json
|
||||
serv += j["response"]["servers"]
|
||||
except Exception as e:
|
||||
# TODO: save exception text
|
||||
print(e)
|
||||
logger.critical(e)
|
||||
self.cleanup_func = CleanupFunc(self.cleanup_on_failure)
|
||||
|
||||
"""
|
||||
TODO: e.g. class method like 'total'
|
||||
total of 10 params for this call
|
||||
@call_on_thread(msg, jobs=10)
|
||||
if total > 1, pop progress bar
|
||||
GLib.idle_add(lambda: self.wait_dialog.format_secondary_text("Pass 2"))
|
||||
GLib.idle_add(lambda: self.wait_dialog.prog.set_fraction(0.5))
|
||||
subclass or wrap add fraction and increment, then calculate off of total
|
||||
This next step is allowed to fail, since this metadata is incidental
|
||||
"""
|
||||
# NOTE: This step is allowed to fail, since this metadata is incidental
|
||||
res = Servers.query_api(key, APPID_DAYZ_EXP, "")
|
||||
if res.status == 200 and res.parsed is True:
|
||||
j = res.json
|
||||
serv += j["response"]["servers"]
|
||||
|
||||
GLib.idle_add(
|
||||
lambda: self.wait_dialog.format_secondary_text("Unpacking servers")
|
||||
)
|
||||
GLib.idle_add(lambda: self.wait_dialog.prog.set_fraction(10 / total))
|
||||
|
||||
# TODO: additional ping column pass, collated
|
||||
GLib.idle_add(lambda: self.wait_dialog.increment("Unpacking servers"))
|
||||
parsed = Servers.parse_json(serv)
|
||||
self.push_data_success(parsed, FilterMode.INITIAL)
|
||||
|
||||
@ -466,7 +450,6 @@ class Controller(GObject.GObject):
|
||||
return value
|
||||
|
||||
def copy_name(self, path: Gtk.TreePath) -> None:
|
||||
# TODO: column values are deterministic, perhaps use a col name to index map
|
||||
name = self.get_col_value_by_path_index(path, 0)
|
||||
if name is None:
|
||||
return
|
||||
@ -659,13 +642,13 @@ class Controller(GObject.GObject):
|
||||
sel.select_path(path)
|
||||
|
||||
# TODO: make as method of tree?
|
||||
# FIXME: move cursor when finished
|
||||
def uncolorize_mods(self) -> None:
|
||||
model = self.get_mod_store()
|
||||
for mod in model:
|
||||
it = mod.iter
|
||||
path = model.get_path(it)
|
||||
model[path][4] = None
|
||||
self.mediator.modtreeview.set_cursor(0)
|
||||
|
||||
def dump_test_2(self) -> None:
|
||||
import time
|
||||
@ -833,8 +816,10 @@ class Controller(GObject.GObject):
|
||||
return self.get_active_treeview().get_map_man()
|
||||
|
||||
def cleanup_on_success(self) -> None:
|
||||
self.pending_jobs = 1
|
||||
treeview = self.get_active_treeview()
|
||||
treeview.set_loaded(True)
|
||||
|
||||
treeview.set_model(self.to_insert)
|
||||
map_man = treeview.get_map_man()
|
||||
|
||||
@ -842,6 +827,7 @@ class Controller(GObject.GObject):
|
||||
# model insertion after thread closes
|
||||
# cf. servers_loaded signal
|
||||
|
||||
# TODO: servers_loaded vs servers_reloaded
|
||||
context = self.get_active_context()
|
||||
self.emitter.emit("servers_loaded", context)
|
||||
|
||||
@ -856,27 +842,29 @@ class Controller(GObject.GObject):
|
||||
|
||||
def cleanup_on_failure(self) -> None:
|
||||
treeview = self.get_active_treeview()
|
||||
treeview.set_model(None)
|
||||
treeview.set_loaded(True)
|
||||
map_man = treeview.get_map_man()
|
||||
# TODO: what if refresh action occurred, and the old model is still valid?
|
||||
|
||||
# TODO: disable map, keyword, and filter widgets if model is None
|
||||
# -> signal driven (servers_empty)
|
||||
map_man.set_unique_maps(None)
|
||||
# TODO: what if refresh action occurred and failed, and the old model is still valid?
|
||||
# skip the step below if refresh action failed
|
||||
# do not wipe control model in this case
|
||||
# e.g. if treeview.is_refresh():
|
||||
# revert old model
|
||||
# wipe refresh state to False
|
||||
|
||||
context = self.get_active_context()
|
||||
# TODO: distinguish signals, e.g. "servers_failed_to_load"
|
||||
self.emitter.emit("servers_loaded", context)
|
||||
treeview.set_model(None)
|
||||
treeview.grab_focus()
|
||||
dialog = ExceptionDialog(self, "API TIMEOUT")
|
||||
dialog.run()
|
||||
|
||||
# def push_data_failure(self) -> None:
|
||||
# #treeview = self.get_active_treeview()
|
||||
# #treeview.set_loaded(True)
|
||||
# # TODO: wipe control model on failure or keep old results?
|
||||
# # manager = treeview.get_filter_man()
|
||||
# self.cleanup_func = CleanupFunc(self.cleanup_on_failure)
|
||||
map_man.set_unique_maps(None)
|
||||
context = self.get_active_context()
|
||||
|
||||
# TODO: distinguish signals, e.g. "servers_failed_to_load"
|
||||
# may trigger different behavior
|
||||
self.emitter.emit("servers_loaded", context)
|
||||
dialog = ExceptionDialog(self, strings.api_warn_msg)
|
||||
dialog.run()
|
||||
|
||||
def push_data_success(self, data: tuple, mode: Optional[FilterMode]) -> None:
|
||||
# FIXME: set outside of thread
|
||||
@ -901,6 +889,7 @@ class Controller(GObject.GObject):
|
||||
self.cleanup_func = CleanupFunc(self.cleanup_on_success)
|
||||
|
||||
def highlight_stale_cleanup(self, stale_mods: list) -> None:
|
||||
"""Manipulates attached ListStore in the main event loop"""
|
||||
model = self.get_mod_store()
|
||||
for mod in model:
|
||||
it = mod.iter
|
||||
@ -911,6 +900,7 @@ class Controller(GObject.GObject):
|
||||
|
||||
@call_on_thread(strings.dialog.working)
|
||||
def highlight_stale(self) -> None:
|
||||
# TODO: set progress bar for number of mods
|
||||
stale = find_stale_mods(self.prefs.paths.config)
|
||||
self.cleanup_func = CleanupFunc(self.highlight_stale_cleanup, stale)
|
||||
|
||||
@ -995,6 +985,7 @@ class Controller(GObject.GObject):
|
||||
|
||||
@call_on_thread(strings.dialog.fetching)
|
||||
def run_query_func(self, func: Callable) -> None:
|
||||
# TODO: use CleanupFunc -> StoredFunc
|
||||
func()
|
||||
|
||||
@call_on_thread(strings.dialog.filtering)
|
||||
@ -1010,6 +1001,7 @@ class Controller(GObject.GObject):
|
||||
# TODO: call filter_man methods directly
|
||||
def refilter_model(self, mode: FilterMode, label: Optional[str] = None) -> None:
|
||||
tv = self.get_active_treeview()
|
||||
#tv.freeze_child_notify()
|
||||
filter_man = tv.get_filter_man()
|
||||
if filter_man.get_control() is None:
|
||||
return
|
||||
@ -1022,7 +1014,7 @@ class Controller(GObject.GObject):
|
||||
self.emitter.emit("servers_loaded", treeview.get_enum())
|
||||
return
|
||||
|
||||
func = treeview.get_query_func()
|
||||
func, jobs = treeview.get_query_func()
|
||||
if func is None:
|
||||
self.emitter.emit("servers_loaded", treeview.get_enum())
|
||||
treeview.set_model(None)
|
||||
@ -1031,8 +1023,10 @@ class Controller(GObject.GObject):
|
||||
# TODO: clear ephemeral model if necessary
|
||||
# manager = treeview.get_filter_man()
|
||||
# manager.clear_model()
|
||||
#treeview.fancy_col.set_cell_data_func(treeview.fancy_rend, None)
|
||||
|
||||
self.first_iteration = True
|
||||
self.pending_jobs = jobs
|
||||
self.run_query_func(func)
|
||||
|
||||
def get_favorite(self) -> tuple[str, str] | tuple[None, None]:
|
||||
@ -1080,6 +1074,27 @@ class Controller(GObject.GObject):
|
||||
map_man = self.get_map_man()
|
||||
return map_man.get_prior_map()
|
||||
|
||||
def get_active_map(self) -> None:
|
||||
return self.get_map_man().get_active_map()
|
||||
|
||||
def set_active_map(self, ind: int) -> None:
|
||||
self.get_map_man().set_active_map(ind)
|
||||
|
||||
def _on_servers_loaded_init(self, emitter: "Emitter") -> None:
|
||||
# FIXME: wipe maps store when changing tabs if model is none
|
||||
tv = self.get_active_treeview()
|
||||
if tv.loaded is False:
|
||||
return
|
||||
store = self.get_map_store()
|
||||
self.emitter.emit("load_maps", store)
|
||||
|
||||
def _on_servers_loaded(self, emitter: "Emitter", tab: "ServerTab") -> None:
|
||||
# NOTE: workaround for GTK bug where fullscreen causes headers to vanish when model is None
|
||||
state = self.has_server_model()
|
||||
tv = self.get_active_treeview()
|
||||
tv.set_headers_visible(state)
|
||||
tv.set_headers_clickable(state)
|
||||
|
||||
def _on_check_toggled(self, emitter: Emitter, label: str, state: bool) -> None:
|
||||
map_man = self.get_map_man()
|
||||
map_man.set_filter(label, state)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import datetime
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
from warnings import deprecated
|
||||
|
||||
from dzgui.const.enum import FilterMode
|
||||
@ -114,16 +115,18 @@ class FilteredModelManager:
|
||||
case FilterMode.TOGGLE_ON:
|
||||
rows = self.filter_toggle_on(filters, *args)
|
||||
|
||||
if mode is not FilterMode.INITIAL:
|
||||
for row in rows:
|
||||
if row[7] in self.ping_cache:
|
||||
row[9] = self.ping_cache[row[7]]
|
||||
# if mode is not FilterMode.INITIAL:
|
||||
# for row in rows:
|
||||
# if row[7] in self.ping_cache:
|
||||
# row[9] = self.ping_cache[row[7]]
|
||||
|
||||
clone = self.new_model_from_class(ServerColumns)
|
||||
if len(rows) > 0:
|
||||
# TODO: considering exposing an option to not sort rows
|
||||
rows = self.sort_rows(rows)
|
||||
for row in rows:
|
||||
clone.append(row)
|
||||
|
||||
# else:
|
||||
# print("nothing to filter, sending none")
|
||||
# clone = None
|
||||
@ -256,7 +259,7 @@ class FilteredModelManager:
|
||||
def convert_model_to_list(self, model: ListStore) -> list:
|
||||
return [[el for el in row] for row in model]
|
||||
|
||||
def set_filtered(self, rows: list | None) -> None:
|
||||
def set_filtered(self, rows: Optional[list]) -> None:
|
||||
if rows is None:
|
||||
rows = []
|
||||
self.filtered = rows
|
||||
|
||||
@ -31,12 +31,19 @@ class MapManager:
|
||||
strings.filter_unofficial: True,
|
||||
strings.filter_modded: True,
|
||||
}
|
||||
self.active_map = 0
|
||||
self.filters: list
|
||||
self.enabled_filters = dict(self.default_filters)
|
||||
|
||||
def reinit_filters(self) -> None:
|
||||
self.enabled_filters = dict(self.default_filters)
|
||||
|
||||
def get_active_map(self) -> int:
|
||||
return self.active_map
|
||||
|
||||
def set_active_map(self, ind: int) -> None:
|
||||
self.active_map = ind
|
||||
|
||||
def get_default_filters(self) -> dict:
|
||||
"""Deep copy of defaults"""
|
||||
return dict(self.default_filters)
|
||||
|
||||
@ -99,10 +99,8 @@ steam_missing = "Local Steam installation is not set, possibly malformed config
|
||||
build_corrupted = (
|
||||
"Steam settings or DayZ installation may be corrupted. Try restarting Steam."
|
||||
)
|
||||
api_warn_msg = """No servers returned. Possible causes:
|
||||
no servers in favorites/history, local network issue, or API key on cooldown.
|
||||
Return to the main menu, wait 30s, and try again.
|
||||
If this issue persists, your API key may be defunct.
|
||||
api_warn_msg = """No servers returned. Please wait and try again.
|
||||
If this issue persists, your API key may be defunct or your network is blocking requests.
|
||||
"""
|
||||
server_timeout = "Timed out when querying server, check IP or try again later"
|
||||
server_error = (
|
||||
@ -167,7 +165,7 @@ ping_tooltip = (
|
||||
)
|
||||
|
||||
# statusbar_helptext = "Select a row to see its detailed description"
|
||||
#statusbar_helptext = "No server metadata to list."
|
||||
# statusbar_helptext = "No server metadata to list."
|
||||
|
||||
# use e.g. filters.1pp
|
||||
filter_1pp = "1PP"
|
||||
|
||||
@ -153,7 +153,6 @@ class FilterPanel(Gtk.Box):
|
||||
"request_maps_focus", lambda _: self.maps_entry.grab_focus()
|
||||
)
|
||||
self.emitter.connect("check_button_pressed", self.toggle_check_by_key)
|
||||
# self.emitter.connect("servers_loaded", self._on_servers_loaded)
|
||||
self.emitter.connect("load_maps", self._on_maps_loaded)
|
||||
|
||||
# TODO: break into MapsCombo class
|
||||
@ -177,6 +176,9 @@ class FilterPanel(Gtk.Box):
|
||||
self.maps_combo.connect("changed", self._on_map_changed)
|
||||
self.maps_combo.connect("key-press-event", self._on_combo_keypress)
|
||||
|
||||
# FIXME: should be a property of treeview's meta manager
|
||||
self.active_map = 0
|
||||
|
||||
for el in (
|
||||
self.filters_label,
|
||||
self.keyword_entry,
|
||||
@ -194,10 +196,12 @@ class FilterPanel(Gtk.Box):
|
||||
if text == row[0]:
|
||||
self.maps_combo.set_active(i)
|
||||
self._on_map_changed(self.maps_combo)
|
||||
self.controller.set_active_map(i)
|
||||
|
||||
def _on_maps_loaded(self, emitter: "Emitter", store: Gtk.ListStore) -> None:
|
||||
self.maps_combo.set_model(store)
|
||||
self.maps_combo.set_active(0)
|
||||
ind = self.controller.get_active_map()
|
||||
self.maps_combo.set_active(ind)
|
||||
self.button_grid.reload_filters()
|
||||
|
||||
# TODO: move into metamanager
|
||||
@ -259,6 +263,8 @@ class FilterPanel(Gtk.Box):
|
||||
store = self.controller.get_map_store()
|
||||
if len(text) >= completion.get_minimum_key_length():
|
||||
completion.set_model(store)
|
||||
ind = self.get_active_combo()
|
||||
self.controller.set_active_map(ind)
|
||||
|
||||
def restore_focus_to_treeview(self) -> Literal[False]:
|
||||
view = self.controller.get_active_treeview()
|
||||
|
||||
@ -44,9 +44,10 @@ class RightPanel(Gtk.Box):
|
||||
state = self.controller.has_server_model()
|
||||
for widget in (self.refresh_button, self.filters_vbox):
|
||||
widget.set_sensitive(state)
|
||||
|
||||
# FIXME: may wish to reload these in place to show new data
|
||||
if context in (ServerTab.RECENT, ServerTab.SAVED):
|
||||
self.refresh_button.set_sensitive(False)
|
||||
# if context in (ServerTab.RECENT, ServerTab.SAVED):
|
||||
# self.refresh_button.set_sensitive(False)
|
||||
|
||||
# TODO: reference for ping pass
|
||||
# def _on_ping_clicked(self, button: Gtk.Button) -> None:
|
||||
|
||||
@ -1,83 +0,0 @@
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa
|
||||
|
||||
from dzgui.const.constants import NO_EXPAND, NO_FILL, NO_PADDING
|
||||
from dzgui.const.enum import Popup
|
||||
|
||||
|
||||
class EntryDialog(GenericDialog):
|
||||
def __init__(self, text: str, mode: Popup, link: str, button_type=None):
|
||||
super().__init__(text, mode)
|
||||
|
||||
"""
|
||||
Wraps Gtk.Entry in a dialog and provides basic response handling.
|
||||
Returns user input as a string or None.
|
||||
The Entry widget itself can be manipulated via the get_entry() method.
|
||||
"""
|
||||
|
||||
self.dialog = GenericDialog(text, mode)
|
||||
self.dialogBox = self.dialog.get_content_area()
|
||||
self.dialog.set_default_response(Gtk.ResponseType.OK)
|
||||
self.dialog.set_size_request(500, 0)
|
||||
|
||||
self.user_entry = Gtk.Entry()
|
||||
set_surrounding_margins(self.user_entry, 20)
|
||||
self.user_entry.set_margin_top(0)
|
||||
self.user_entry.set_size_request(250, 0)
|
||||
self.user_entry.set_activates_default(True)
|
||||
self.dialogBox.pack_start(self.user_entry, NO_EXPAND, NO_FILL, NO_PADDING)
|
||||
|
||||
if link:
|
||||
button = Gtk.Button(label=link)
|
||||
button.set_margin_start(60)
|
||||
button.set_margin_end(60)
|
||||
button.connect("clicked", self._on_button_clicked, button_type)
|
||||
self.dialogBox.pack_end(button, NO_EXPAND, NO_FILL, NO_PADDING)
|
||||
|
||||
self.ok = self.dialog.action_area.get_children()[1]
|
||||
self.ok.set_sensitive(False)
|
||||
self.user_entry.connect("insert-text", self._on_text_typed)
|
||||
self.user_entry.get_property("buffer").connect(
|
||||
"deleted-text", self._on_text_deleted
|
||||
)
|
||||
|
||||
def _is_valid_text(self, text: str) -> bool:
|
||||
if text.isspace():
|
||||
return False
|
||||
if len(text) == 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _on_text_deleted(
|
||||
self, buffer: Gtk.EntryBuffer, position: int, chars: int
|
||||
) -> None:
|
||||
text = buffer.get_text()
|
||||
state = self._is_valid_text(text)
|
||||
self.ok.set_sensitive(state)
|
||||
|
||||
def _on_text_typed(
|
||||
self, entry: Gtk.Entry, text: str, length: int, pos: int
|
||||
) -> None:
|
||||
state = self._is_valid_text(text)
|
||||
self.ok.set_sensitive(state)
|
||||
|
||||
def _on_button_clicked(self, button: Gtk.Button, enum: RowType) -> None:
|
||||
result = open_links.open_link_by_rowtype(enum)
|
||||
if result is False:
|
||||
AppNav.window.spawn_dialog(strings.something_wrong, Popup.NOTIFY)
|
||||
|
||||
def get_entry(self) -> Gtk.Entry:
|
||||
return self.user_entry
|
||||
|
||||
def get_input(self) -> str | None:
|
||||
self.dialog.show_all()
|
||||
|
||||
response = self.dialog.run()
|
||||
text = self.user_entry.get_text()
|
||||
self.dialog.destroy()
|
||||
if (response == Gtk.ResponseType.OK) and (text != ""):
|
||||
return text
|
||||
else:
|
||||
return None
|
||||
@ -101,9 +101,7 @@ class NotifyDialog(GenericDialog):
|
||||
|
||||
|
||||
class WaitDialog(GenericDialog):
|
||||
def __init__(
|
||||
self, controller: "Controller", secondary: str, show_progress: bool = False
|
||||
):
|
||||
def __init__(self, controller: "Controller", secondary: str, jobs: int = 1):
|
||||
super().__init__(
|
||||
controller=controller,
|
||||
text=strings.wait,
|
||||
@ -112,16 +110,26 @@ class WaitDialog(GenericDialog):
|
||||
secondary=secondary,
|
||||
)
|
||||
|
||||
self.jobs = jobs
|
||||
self.cur_job = 1
|
||||
|
||||
self.connect("delete-event", self._on_dialog_delete)
|
||||
content = self.get_content_area()
|
||||
spinner = Gtk.Spinner()
|
||||
self.prog = Gtk.ProgressBar()
|
||||
content.pack_end(spinner, NO_EXPAND, NO_FILL, 0)
|
||||
if show_progress:
|
||||
if self.jobs > 1:
|
||||
content.pack_end(self.prog, NO_EXPAND, NO_FILL, 0)
|
||||
else:
|
||||
spinner.start()
|
||||
|
||||
def increment(self, msg: str = "") -> None:
|
||||
if msg != "":
|
||||
self.format_secondary_text(msg)
|
||||
fraction = self.cur_job / self.jobs
|
||||
self.prog.set_fraction(fraction)
|
||||
self.cur_job += 1
|
||||
|
||||
def _on_dialog_delete(
|
||||
self, response_id: Gtk.ResponseType, event: Gdk.Event
|
||||
) -> Literal[True]:
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
import textwrap
|
||||
from typing import Self, TYPE_CHECKING
|
||||
|
||||
from dzgui.const.enum import Popup, Preferences
|
||||
from dzgui.const.constants import NO_EXPAND, NO_FILL
|
||||
from dzgui.util.open_links import open_user_workshop
|
||||
from dzgui.util.strings import notice
|
||||
from dzgui.views.dialogs.generic import GenericDialog
|
||||
from dzgui.views.components.buttons import SteamTextButton, WebButton
|
||||
from dzgui.views.components.buttons import SteamTextButton
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa E402
|
||||
|
||||
@ -18,9 +17,17 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
class WorkshopLinkDialog(GenericDialog):
|
||||
def __init__(self, controller: "Controller", text: str, button_label: str, uid: str):
|
||||
def __init__(
|
||||
self, controller: "Controller", text: str, button_label: str, uid: str
|
||||
):
|
||||
text = textwrap.dedent(text).replace("\n", " ")
|
||||
super().__init__(controller, text=notice, mtype=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK, secondary=text)
|
||||
super().__init__(
|
||||
controller,
|
||||
text=notice,
|
||||
mtype=Gtk.MessageType.INFO,
|
||||
buttons=Gtk.ButtonsType.OK,
|
||||
secondary=text,
|
||||
)
|
||||
self.controller = controller
|
||||
self.dialogBox = self.get_content_area()
|
||||
self.set_default_response(Gtk.ResponseType.OK)
|
||||
@ -43,12 +50,10 @@ class WorkshopLinkDialog(GenericDialog):
|
||||
def _on_button_clicked(self, button: Gtk.Button, uid: str) -> None:
|
||||
# TODO: currently checks user id on instantiation
|
||||
self.controller.open_user_workshop(uid)
|
||||
#client = self.controller.query_config(Preferences.CLIENT)
|
||||
#open_user_workshop(uid, client)
|
||||
# client = self.controller.query_config(Preferences.CLIENT)
|
||||
# open_user_workshop(uid, client)
|
||||
|
||||
def _on_dialog_response(
|
||||
self, dialog: Self, resp: Gtk.ResponseType
|
||||
) -> None:
|
||||
def _on_dialog_response(self, dialog: Self, resp: Gtk.ResponseType) -> None:
|
||||
match resp:
|
||||
case Gtk.ResponseType.DELETE_EVENT:
|
||||
return
|
||||
|
||||
@ -20,27 +20,21 @@ class ContextMixin(TreeView):
|
||||
) -> None:
|
||||
# FIXME: double click causes issue
|
||||
# split into present by click and present by key
|
||||
|
||||
if self.is_selection_empty():
|
||||
return False
|
||||
|
||||
if event.type is Gdk.EventType.BUTTON_PRESS:
|
||||
if event.button != 3:
|
||||
return False
|
||||
try:
|
||||
pathinfo = self.get_path_at_pos(int(event.x), int(event.y))
|
||||
if pathinfo is None:
|
||||
return True
|
||||
(path, col, cellx, celly) = pathinfo
|
||||
if path is None:
|
||||
return True
|
||||
self.set_cursor(path, col, False)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if event.type is Gdk.EventType.KEY_PRESS:
|
||||
if event.state is not Gdk.ModifierType.CONTROL_MASK:
|
||||
return False
|
||||
if event.keyval is not Gdk.KEY_l:
|
||||
match event.type:
|
||||
case Gdk.EventType.BUTTON_PRESS:
|
||||
if event.button != 3:
|
||||
return False
|
||||
self._process_button_event(event)
|
||||
case Gdk.EventType.KEY_PRESS:
|
||||
if event.state is not Gdk.ModifierType.CONTROL_MASK:
|
||||
return False
|
||||
if event.keyval is not Gdk.KEY_l:
|
||||
return False
|
||||
case _:
|
||||
return False
|
||||
|
||||
group = self.menu
|
||||
@ -64,6 +58,18 @@ class ContextMixin(TreeView):
|
||||
|
||||
self.context_menu.select_first(False)
|
||||
|
||||
def _process_button_event(self, event: Gdk.EventButton) -> None:
|
||||
try:
|
||||
pathinfo = self.get_path_at_pos(int(event.x), int(event.y))
|
||||
if pathinfo is None:
|
||||
return True
|
||||
(path, col, cellx, celly) = pathinfo
|
||||
if path is None:
|
||||
return True
|
||||
self.set_cursor(path, col, False)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def _on_menu_click(self, widget: Gtk.MenuItem, enum: ContextMenu) -> None:
|
||||
"""
|
||||
Local mods page allows multi selection, so ensure that only focused row
|
||||
|
||||
@ -37,8 +37,9 @@ class ServerNotebook(Gtk.ScrolledWindow):
|
||||
self.lan = ServerTreeView(controller, ServerTab.LAN, ContextMenuGroup.SCAN_LAN)
|
||||
|
||||
self.recent.set_query_func(self.controller.dump_test_2)
|
||||
# TODO: number of servers will not be known a priori until reading file during query phase
|
||||
self.lan.set_query_func(self.controller.dump_test_2)
|
||||
self.browser.set_query_func(self.controller.dump_api)
|
||||
self.browser.set_query_func(self.controller.dump_api, jobs=11)
|
||||
|
||||
tabs = [
|
||||
(self.browser, server_labels.browser),
|
||||
|
||||
@ -7,12 +7,14 @@ from warnings import deprecated
|
||||
from dzgui.views.mixins.context_mixin import ContextMixin
|
||||
from dzgui.const.enum import ContextMenuGroup, ServerTab
|
||||
from dzgui.api.servers import Record
|
||||
from dzgui.api.servers import ping as Ping
|
||||
from dzgui.model.map_model import MapManager
|
||||
from dzgui.model.filtered_model import FilteredModelManager
|
||||
from dzgui.util.dist import CalcDist
|
||||
from dzgui.util import strings
|
||||
from typing import Callable, Literal, TYPE_CHECKING
|
||||
|
||||
|
||||
from dzgui.views.trees.tree_base import TreeView
|
||||
import dzgui.util._json as JSON # noqa
|
||||
|
||||
@ -56,6 +58,9 @@ class ServerTreeView(ContextMixin, TreeView):
|
||||
|
||||
self.queue_id: int
|
||||
self.handler_id: int
|
||||
self.query_func_jobs = 1
|
||||
|
||||
self.seen_cache = []
|
||||
|
||||
self.current_proc = None
|
||||
self.queue = multiprocessing.Queue()
|
||||
@ -95,17 +100,21 @@ class ServerTreeView(ContextMixin, TreeView):
|
||||
else:
|
||||
w = width_map[column_title]
|
||||
column.set_fixed_width(w)
|
||||
#if column_title == "Name":
|
||||
#if column_title == "Ping":
|
||||
#self.fancy_col = column
|
||||
#self.fancy_rend = renderer
|
||||
#column.set_cell_data_func(renderer, self._get_ping)
|
||||
# if column_title == "Name":
|
||||
# column.set_fixed_width(800)
|
||||
#if column_title == "Map":
|
||||
# if column_title == "Map":
|
||||
# column.set_fixed_width(300)
|
||||
|
||||
# TODO: standardize widths based on column title and longest content
|
||||
#if column_title == "Name":
|
||||
# if column_title == "Name":
|
||||
# column.set_fixed_width(500)
|
||||
#if column_title == "Map":
|
||||
# if column_title == "Map":
|
||||
# column.set_fixed_width(200)
|
||||
#if column_title == "IP":
|
||||
# if column_title == "IP":
|
||||
# column.set_fixed_width(240)
|
||||
|
||||
column.connect("notify::fixed-width", self._on_col_width_changed)
|
||||
@ -119,55 +128,47 @@ class ServerTreeView(ContextMixin, TreeView):
|
||||
self.connect("map", self._on_map)
|
||||
self.connect("unmap", self._on_unmap)
|
||||
|
||||
self.emitter.connect("servers_loaded_init", self._on_servers_loaded_init)
|
||||
self.emitter.connect("servers_loaded", self._on_servers_loaded)
|
||||
def _get_ping(
|
||||
self,
|
||||
column: Gtk.TreeViewColumn,
|
||||
cell: Gtk.CellRendererText,
|
||||
model: Gtk.TreeModel,
|
||||
it: Gtk.TreeIter,
|
||||
data: Any,
|
||||
):
|
||||
def ping_server(model, _iter, ip: str, qport: int, ping: int):
|
||||
#res = Ping(0, model[_iter])
|
||||
res = Ping(0, ip, qport, ping)
|
||||
ping = res.ping
|
||||
print(res)
|
||||
GLib.idle_add(lambda: model.set(_iter, ping_column, ping))
|
||||
|
||||
def _on_servers_loaded(self, emitter: "Emitter", tab: "ServerTab") -> None:
|
||||
# FIXME: gets called on every tab, should be localized only to that context
|
||||
# hook signal from main controller
|
||||
state = self.controller.has_server_model()
|
||||
# NOTE: workaround for GTK bug where fullscreen causes headers to vanish when model is None
|
||||
self.set_headers_visible(state)
|
||||
self.set_headers_clickable(state)
|
||||
addr_column = 7
|
||||
qport_column = 8
|
||||
ping_column = 9
|
||||
# TODO: unpack model before entering thread
|
||||
# test 40k servers with unpacked model
|
||||
addr = model.get_value(it, addr_column).split(":")[0]
|
||||
qport = model.get_value(it, qport_column)
|
||||
ping = model.get_value(it, ping_column)
|
||||
ip = f"{addr}:{qport}"
|
||||
|
||||
def _on_servers_loaded_init(self, emitter: "Emitter") -> None:
|
||||
if self.loaded is False:
|
||||
if ip in self.seen_cache:
|
||||
return
|
||||
store = self.map_man.get_map_store()
|
||||
self.emitter.emit("load_maps", store)
|
||||
self.seen_cache.append(ip)
|
||||
|
||||
#def _get_ping(
|
||||
# self,
|
||||
# column: Gtk.TreeViewColumn,
|
||||
# cell: Gtk.CellRendererText,
|
||||
# model: Gtk.TreeModel,
|
||||
# it: Gtk.TreeIter,
|
||||
# data: Any,
|
||||
#):
|
||||
# def ping(model, it, ip: str):
|
||||
# # TODO: use a2s to ping server
|
||||
# # a2s.info -> "ping" key
|
||||
# # Servers.ping() -> this accepts a whole row
|
||||
# # try, if failure just abort
|
||||
# self.res = ip.replace(":", "%")
|
||||
# num = 11111
|
||||
# GLib.idle_add(lambda: model.set(it, 9, num))
|
||||
|
||||
# addr = model.get_value(it, 7).split(":")[0]
|
||||
# qport = model.get_value(it, 8)
|
||||
# ip = f"{addr}:{qport}"
|
||||
|
||||
# thread = threading.Thread(
|
||||
# daemon=True,
|
||||
# target=ping,
|
||||
# args=(
|
||||
# model,
|
||||
# it,
|
||||
# ip,
|
||||
# ),
|
||||
# )
|
||||
# thread.start()
|
||||
# pass
|
||||
thread = threading.Thread(
|
||||
daemon=True,
|
||||
target=ping_server,
|
||||
args=(
|
||||
model,
|
||||
it,
|
||||
ip,
|
||||
qport,
|
||||
ping
|
||||
),
|
||||
)
|
||||
thread.start()
|
||||
|
||||
def start_timeout(self) -> None:
|
||||
self.queue_id = GLib.timeout_add(QUEUE_CHECK_DELAY, self._check_result_queue)
|
||||
@ -178,7 +179,7 @@ class ServerTreeView(ContextMixin, TreeView):
|
||||
def get_filter_man(self) -> FilteredModelManager:
|
||||
return self.filter_man
|
||||
|
||||
#def shrink_to_fit(self) -> None:
|
||||
# def shrink_to_fit(self) -> None:
|
||||
# cols = self.get_columns()
|
||||
# # TODO: run on only one treeview and propagate results
|
||||
# # TODO: does not shrink name, map, ip fields to fit
|
||||
@ -208,8 +209,8 @@ class ServerTreeView(ContextMixin, TreeView):
|
||||
if self.get_enum() is ServerTab.LAN:
|
||||
self.emitter.emit("lan_tab_toggled", True)
|
||||
|
||||
# FIXME: only if the tab is active
|
||||
store = self.map_man.get_map_store()
|
||||
# FIXME: if model is none, wipe maps
|
||||
self.emitter.emit("load_maps", store)
|
||||
self.handler_id = self.emitter.connect("statusbar_loaded", self.start_distcalc)
|
||||
self.start_timeout()
|
||||
@ -222,11 +223,12 @@ class ServerTreeView(ContextMixin, TreeView):
|
||||
if self.get_enum() is ServerTab.LAN:
|
||||
self.emitter.emit("lan_tab_toggled", False)
|
||||
|
||||
def set_query_func(self, func: Callable) -> None:
|
||||
def set_query_func(self, func: Callable, jobs: int = 1) -> None:
|
||||
self.query_func = func
|
||||
self.query_func_jobs = jobs
|
||||
|
||||
def get_query_func(self) -> Callable | None:
|
||||
return self.query_func
|
||||
return self.query_func, self.query_func_jobs
|
||||
|
||||
def _on_col_width_changed(
|
||||
self, col: Gtk.TreeViewColumn, width: GObject.ParamSpecInt
|
||||
|
||||
Loading…
Reference in New Issue
Block a user