mirror of
https://github.com/aclist/dztui.git
synced 2026-10-31 07:02:50 +01:00
chore: abstract filter panel components
This commit is contained in:
1 parent
b14c63b23c
commit
d4b346006d
6 files changed
+370
-166
No files matched your search
@@ -83,6 +83,10 @@ class Emitter(GObject.GObject):
|
|||||||
) -> None:
|
) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@GObject.Signal(flags=GObject.SignalFlags.RUN_LAST, arg_types=(str,bool,))
|
||||||
|
def check_toggled(self, label: str, state: bool) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
@GObject.Signal(flags=GObject.SignalFlags.RUN_LAST, arg_types=(object,))
|
@GObject.Signal(flags=GObject.SignalFlags.RUN_LAST, arg_types=(object,))
|
||||||
def servers_loaded(self, tag: "ServerTab") -> None:
|
def servers_loaded(self, tag: "ServerTab") -> None:
|
||||||
pass
|
pass
|
||||||
|
|||||||
+197
-37
@@ -23,7 +23,13 @@ from dzgui.api.mods import (
|
|||||||
_hash,
|
_hash,
|
||||||
remove_stale_signatures,
|
remove_stale_signatures,
|
||||||
)
|
)
|
||||||
from dzgui.const.enum import FilterMode, Preferences, NotebookPage, ButtonType, ContextMenu
|
from dzgui.const.enum import (
|
||||||
|
FilterMode,
|
||||||
|
Preferences,
|
||||||
|
NotebookPage,
|
||||||
|
ButtonType,
|
||||||
|
ContextMenu,
|
||||||
|
)
|
||||||
|
|
||||||
from dzgui.const.constants import (
|
from dzgui.const.constants import (
|
||||||
APPID_DAYZ,
|
APPID_DAYZ,
|
||||||
@@ -56,6 +62,7 @@ from gi.repository import Gtk, Gdk, GLib, GObject # noqa E402
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from dzgui.model.map_model import MapManager
|
||||||
from dzgui.const.enum import ServerTab
|
from dzgui.const.enum import ServerTab
|
||||||
from dzgui.util.dist import Haversine
|
from dzgui.util.dist import Haversine
|
||||||
from dzgui.views.base import Notebook, Grid, OuterWindow
|
from dzgui.views.base import Notebook, Grid, OuterWindow
|
||||||
@@ -95,11 +102,11 @@ class Controller(GObject.GObject):
|
|||||||
self.model_man = ModelManager()
|
self.model_man = ModelManager()
|
||||||
self.emitter = Emitter()
|
self.emitter = Emitter()
|
||||||
self.emitter.connect("map_selection_changed", self._on_map_selection_changed)
|
self.emitter.connect("map_selection_changed", self._on_map_selection_changed)
|
||||||
|
self.emitter.connect("check_toggled", self._on_check_toggled)
|
||||||
|
|
||||||
# NOTE: suppress requests until entire UI is loaded
|
# NOTE: suppress requests until entire UI is loaded
|
||||||
self.loaded = False
|
self.loaded = False
|
||||||
|
|
||||||
|
|
||||||
def get_emitter(self) -> Emitter:
|
def get_emitter(self) -> Emitter:
|
||||||
return self.emitter
|
return self.emitter
|
||||||
|
|
||||||
@@ -112,7 +119,9 @@ class Controller(GObject.GObject):
|
|||||||
self.wait_dialog.show_all()
|
self.wait_dialog.show_all()
|
||||||
thread = threading.Thread(target=func, args=args)
|
thread = threading.Thread(target=func, args=args)
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
def register_widget(self, attr: str, widget: Gtk.Widget) -> None:
|
def register_widget(self, attr: str, widget: Gtk.Widget) -> None:
|
||||||
@@ -250,10 +259,10 @@ class Controller(GObject.GObject):
|
|||||||
context = self.get_active_context()
|
context = self.get_active_context()
|
||||||
page = self.mediator.notebook.get_page_by_enum()
|
page = self.mediator.notebook.get_page_by_enum()
|
||||||
if page != NotebookPage.SERVERS:
|
if page != NotebookPage.SERVERS:
|
||||||
self.emitter.emit("distcalc_ended" , None, context)
|
self.emitter.emit("distcalc_ended", None, context)
|
||||||
return
|
return
|
||||||
if enum != context:
|
if enum != context:
|
||||||
self.emitter.emit("distcalc_ended" , None, context)
|
self.emitter.emit("distcalc_ended", None, context)
|
||||||
return
|
return
|
||||||
|
|
||||||
dist: str
|
dist: str
|
||||||
@@ -357,10 +366,7 @@ class Controller(GObject.GObject):
|
|||||||
params = Servers.params
|
params = Servers.params
|
||||||
serv = []
|
serv = []
|
||||||
with ThreadPoolExecutor() as executor:
|
with ThreadPoolExecutor() as executor:
|
||||||
futures = [
|
futures = [executor.submit(job, key, APPID_DAYZ, param) for param in params]
|
||||||
executor.submit(job, key, APPID_DAYZ, param)
|
|
||||||
for param in params
|
|
||||||
]
|
|
||||||
wait(futures)
|
wait(futures)
|
||||||
for future in futures:
|
for future in futures:
|
||||||
res = future.result()
|
res = future.result()
|
||||||
@@ -368,7 +374,7 @@ class Controller(GObject.GObject):
|
|||||||
# TODO: pop warning dialog, create enum around various failure states
|
# TODO: pop warning dialog, create enum around various failure states
|
||||||
# TODO: if first iteration, disable map combo. otherwise, do nothing
|
# TODO: if first iteration, disable map combo. otherwise, do nothing
|
||||||
self.new_maps = None
|
self.new_maps = None
|
||||||
self.push_data_failure() #None, FilterMode.INITIAL, success=False)
|
self.push_data_failure() # None, FilterMode.INITIAL, success=False)
|
||||||
return
|
return
|
||||||
j = res.json
|
j = res.json
|
||||||
serv += j["response"]["servers"]
|
serv += j["response"]["servers"]
|
||||||
@@ -436,9 +442,9 @@ class Controller(GObject.GObject):
|
|||||||
open_workshop_page(mod, cmd)
|
open_workshop_page(mod, cmd)
|
||||||
|
|
||||||
# TODO: put in model manager (dedicated manager for mod store)
|
# TODO: put in model manager (dedicated manager for mod store)
|
||||||
#def get_mod_from_tree_path(
|
# def get_mod_from_tree_path(
|
||||||
# self, tree_path: Gtk.TreePath
|
# self, tree_path: Gtk.TreePath
|
||||||
#) -> tuple[str, Gtk.TreeIter]:
|
# ) -> tuple[str, Gtk.TreeIter]:
|
||||||
# model = self.model_man.get_mod_store()
|
# model = self.model_man.get_mod_store()
|
||||||
# tree_iter = model.get_iter(tree_path)
|
# tree_iter = model.get_iter(tree_path)
|
||||||
# mod = model.get(tree_iter, 2)[0]
|
# mod = model.get(tree_iter, 2)[0]
|
||||||
@@ -544,7 +550,6 @@ class Controller(GObject.GObject):
|
|||||||
case ContextMenu.OPEN_WORKSHOP:
|
case ContextMenu.OPEN_WORKSHOP:
|
||||||
self.open_mod_page(path)
|
self.open_mod_page(path)
|
||||||
|
|
||||||
|
|
||||||
def toggle_mod_selection(self, state: bool) -> None:
|
def toggle_mod_selection(self, state: bool) -> None:
|
||||||
sel = self.mediator.modtreeview.get_selection()
|
sel = self.mediator.modtreeview.get_selection()
|
||||||
if state:
|
if state:
|
||||||
@@ -596,7 +601,7 @@ class Controller(GObject.GObject):
|
|||||||
it = mod.iter
|
it = mod.iter
|
||||||
path = model.get_path(it)
|
path = model.get_path(it)
|
||||||
# TODO: consider storing in ListStore as int
|
# TODO: consider storing in ListStore as int
|
||||||
# TODO: clone existing model and set outside of thread
|
# TODO: clone existing model and set outside of threai
|
||||||
if int(mod[2]) in stale:
|
if int(mod[2]) in stale:
|
||||||
model[path][4] = HEX_RED
|
model[path][4] = HEX_RED
|
||||||
|
|
||||||
@@ -607,25 +612,172 @@ class Controller(GObject.GObject):
|
|||||||
|
|
||||||
def dump_test_2(self) -> None:
|
def dump_test_2(self) -> None:
|
||||||
import time
|
import time
|
||||||
|
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
data = (
|
data = (
|
||||||
["BAR", "chernarusplus", "a", "a", 1, 1, 1, "185.207.214.16:2302", 1, 1, "a", False],
|
[
|
||||||
["BAR", "chernarusplus", "a", "a", 1, 1, 1, "172.111.51.156:2302", 1, 1, "a", False],
|
"BAR",
|
||||||
["BAR", "chernarusplus", "a", "a", 1, 1, 1, "185.207.214.16:2302", 1, 1, "a", False],
|
"chernarusplus",
|
||||||
["BAR", "chernarusplus", "a", "a", 1, 1, 1, "185.207.214.16:2302", 1, 1, "a", False],
|
"a",
|
||||||
["BAR", "chernarusplus", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
|
"a",
|
||||||
["BAR", "chernarusplus", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
|
1,
|
||||||
["BAR", "chernarusplus", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
|
1,
|
||||||
["BAR", "chernarusplus", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
|
1,
|
||||||
["BAR", "chernarusplus", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
|
"185.207.214.16:2302",
|
||||||
["BAR", "chernarusplus", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
|
1,
|
||||||
["BAR", "chernarusplus", "a", "a", 1, 1, 1, "185.207.214.16:2302", 0, 0, "a", False],
|
1,
|
||||||
|
"a",
|
||||||
|
False,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"BAR",
|
||||||
|
"chernarusplus",
|
||||||
|
"a",
|
||||||
|
"a",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
"172.111.51.156:2302",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
"a",
|
||||||
|
False,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"BAR",
|
||||||
|
"chernarusplus",
|
||||||
|
"a",
|
||||||
|
"a",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
"185.207.214.16:2302",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
"a",
|
||||||
|
False,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"BAR",
|
||||||
|
"chernarusplus",
|
||||||
|
"a",
|
||||||
|
"a",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
"185.207.214.16:2302",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
"a",
|
||||||
|
False,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"BAR",
|
||||||
|
"chernarusplus",
|
||||||
|
"a",
|
||||||
|
"a",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
"185.207.214.16:2302",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"a",
|
||||||
|
False,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"BAR",
|
||||||
|
"chernarusplus",
|
||||||
|
"a",
|
||||||
|
"a",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
"185.207.214.16:2302",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"a",
|
||||||
|
False,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"BAR",
|
||||||
|
"chernarusplus",
|
||||||
|
"a",
|
||||||
|
"a",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
"185.207.214.16:2302",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"a",
|
||||||
|
False,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"BAR",
|
||||||
|
"chernarusplus",
|
||||||
|
"a",
|
||||||
|
"a",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
"185.207.214.16:2302",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"a",
|
||||||
|
False,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"BAR",
|
||||||
|
"chernarusplus",
|
||||||
|
"a",
|
||||||
|
"a",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
"185.207.214.16:2302",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"a",
|
||||||
|
False,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"BAR",
|
||||||
|
"chernarusplus",
|
||||||
|
"a",
|
||||||
|
"a",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
"185.207.214.16:2302",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"a",
|
||||||
|
False,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"BAR",
|
||||||
|
"chernarusplus",
|
||||||
|
"a",
|
||||||
|
"a",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
"185.207.214.16:2302",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"a",
|
||||||
|
False,
|
||||||
|
],
|
||||||
)
|
)
|
||||||
self.push_data(data, FilterMode.INITIAL, success=True)
|
self.push_data(data, FilterMode.INITIAL, success=True)
|
||||||
|
|
||||||
# TODO: eg dedicated cleanup on failure, cleanup on sucess
|
# TODO: eg dedicated cleanup on failure, cleanup on sucess
|
||||||
# these can pop their own predefined dialogs, much simpler
|
# these can pop their own predefined dialogs, much simpler
|
||||||
|
|
||||||
|
def get_map_man(self) -> "MapManager":
|
||||||
|
return self.get_active_treeview().get_map_man()
|
||||||
|
|
||||||
def cleanup(self) -> None:
|
def cleanup(self) -> None:
|
||||||
treeview = self.get_active_treeview()
|
treeview = self.get_active_treeview()
|
||||||
treeview.set_loaded(True)
|
treeview.set_loaded(True)
|
||||||
@@ -636,7 +788,6 @@ class Controller(GObject.GObject):
|
|||||||
# model insertion after thread closes
|
# model insertion after thread closes
|
||||||
# cf. servers_loaded signal
|
# cf. servers_loaded signal
|
||||||
|
|
||||||
|
|
||||||
context = self.get_active_context()
|
context = self.get_active_context()
|
||||||
self.emitter.emit("servers_loaded", context)
|
self.emitter.emit("servers_loaded", context)
|
||||||
|
|
||||||
@@ -647,10 +798,9 @@ class Controller(GObject.GObject):
|
|||||||
self.first_iteration = False
|
self.first_iteration = False
|
||||||
self.new_maps = None
|
self.new_maps = None
|
||||||
|
|
||||||
|
|
||||||
treeview.grab_focus()
|
treeview.grab_focus()
|
||||||
self.destroy_on_idle()
|
self.destroy_on_idle()
|
||||||
#if self.success is False:
|
# if self.success is False:
|
||||||
# # TODO: different dialogs for server tab contexts, e.g. lan timeout
|
# # TODO: different dialogs for server tab contexts, e.g. lan timeout
|
||||||
# # TODO: if history/favorites is empty, don't even trigger a call to dump data
|
# # TODO: if history/favorites is empty, don't even trigger a call to dump data
|
||||||
# dialog = ExceptionDialog(self, "API TIMEOUT")
|
# dialog = ExceptionDialog(self, "API TIMEOUT")
|
||||||
@@ -679,7 +829,7 @@ class Controller(GObject.GObject):
|
|||||||
treeview = self.get_active_treeview()
|
treeview = self.get_active_treeview()
|
||||||
treeview.set_loaded(True)
|
treeview.set_loaded(True)
|
||||||
# TODO: wipe control model on failure or keep old results?
|
# TODO: wipe control model on failure or keep old results?
|
||||||
manager = treeview.get_filter_man()
|
# manager = treeview.get_filter_man()
|
||||||
GLib.idle_add(self.cleanup_on_failure)
|
GLib.idle_add(self.cleanup_on_failure)
|
||||||
|
|
||||||
def push_data(self, data: tuple, mode: Optional[FilterMode], success: bool) -> None:
|
def push_data(self, data: tuple, mode: Optional[FilterMode], success: bool) -> None:
|
||||||
@@ -841,7 +991,7 @@ class Controller(GObject.GObject):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# TODO: maybe set tree to none afterwards (swap models)
|
# TODO: maybe set tree to none afterwards (swap models)
|
||||||
#treeview.set_model(None)
|
# treeview.set_model(None)
|
||||||
func = treeview.get_query_func()
|
func = treeview.get_query_func()
|
||||||
if func is None:
|
if func is None:
|
||||||
self.emitter.emit("servers_loaded", treeview.get_enum())
|
self.emitter.emit("servers_loaded", treeview.get_enum())
|
||||||
@@ -887,23 +1037,33 @@ class Controller(GObject.GObject):
|
|||||||
return self.mediator.filters.get_keyword_filter()
|
return self.mediator.filters.get_keyword_filter()
|
||||||
|
|
||||||
def get_map_store(self) -> Gtk.ListStore:
|
def get_map_store(self) -> Gtk.ListStore:
|
||||||
treeview = self.get_active_treeview()
|
map_man = self.get_map_man()
|
||||||
map_man = treeview.get_map_man()
|
|
||||||
return map_man.get_map_store()
|
return map_man.get_map_store()
|
||||||
|
|
||||||
def get_selected_map(self) -> str:
|
def get_selected_map(self) -> str:
|
||||||
treeview = self.get_active_treeview()
|
map_man = self.get_map_man()
|
||||||
map_man = treeview.get_map_man()
|
|
||||||
return map_man.get_selected_map()
|
return map_man.get_selected_map()
|
||||||
|
|
||||||
|
def get_enabled_filters(self) -> dict:
|
||||||
|
map_man = self.get_map_man()
|
||||||
|
return map_man.get_filters()
|
||||||
|
|
||||||
def get_prior_map(self) -> str:
|
def get_prior_map(self) -> str:
|
||||||
treeview = self.get_active_treeview()
|
map_man = self.get_map_man()
|
||||||
map_man = treeview.get_map_man()
|
|
||||||
return map_man.get_prior_map()
|
return map_man.get_prior_map()
|
||||||
|
|
||||||
|
def _on_check_toggled(self, emitter: Emitter, label: str, state: bool) -> None:
|
||||||
|
map_man = self.get_map_man()
|
||||||
|
map_man.set_filter(label, state)
|
||||||
|
|
||||||
|
if state:
|
||||||
|
mode = FilterMode.TOGGLE_ON
|
||||||
|
else:
|
||||||
|
mode = FilterMode.TOGGLE_OFF
|
||||||
|
self.refilter_model(mode, label)
|
||||||
|
|
||||||
def _on_map_selection_changed(self, emitter: Emitter, selection: str) -> None:
|
def _on_map_selection_changed(self, emitter: Emitter, selection: str) -> None:
|
||||||
treeview = self.get_active_treeview()
|
map_man = self.get_map_man()
|
||||||
map_man = treeview.get_map_man()
|
|
||||||
map_man.set_selected_map(selection)
|
map_man.set_selected_map(selection)
|
||||||
self.refilter_model(FilterMode.MAP)
|
self.refilter_model(FilterMode.MAP)
|
||||||
|
|
||||||
|
|||||||
@@ -417,7 +417,7 @@ options = Options(
|
|||||||
DZGUI will manage mod installation and deletion for you.
|
DZGUI will manage mod installation and deletion for you.
|
||||||
To prevent conflicts with Steam Workshop subscriptions and old mods from being downloaded
|
To prevent conflicts with Steam Workshop subscriptions and old mods from being downloaded
|
||||||
when Steam updates, you should unsubscribe from any existing Workshop mods you manually subscribed to.
|
when Steam updates, you should unsubscribe from any existing Workshop mods you manually subscribed to.
|
||||||
Open your Profile > Workshop Items and select 'Unsubscribe from all'
|
Open your Profile, then 'Workshop Items' and select 'Unsubscribe from all'
|
||||||
on the right-hand side.
|
on the right-hand side.
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,83 +20,151 @@ if TYPE_CHECKING:
|
|||||||
from dzgui.const.enum import ServerTab
|
from dzgui.const.enum import ServerTab
|
||||||
|
|
||||||
|
|
||||||
class FilterPanel(Gtk.Box):
|
class ButtonGrid(Gtk.Grid):
|
||||||
def __init__(self, controller: "Controller") -> None:
|
def __init__(self, controller: "Controller", defaults: dict) -> None:
|
||||||
super().__init__(spacing=6, vexpand=False)
|
super().__init__(
|
||||||
|
|
||||||
self.default_filters = {
|
|
||||||
strings.filter_1pp: True,
|
|
||||||
strings.filter_day: True,
|
|
||||||
strings.filter_empty: False,
|
|
||||||
strings.filter_3pp: True,
|
|
||||||
strings.filter_night: True,
|
|
||||||
strings.filter_full: False,
|
|
||||||
strings.filter_lowpop: True,
|
|
||||||
strings.filter_nonascii: False,
|
|
||||||
strings.filter_duplicate: False,
|
|
||||||
strings.filter_official: True,
|
|
||||||
strings.filter_unofficial: True,
|
|
||||||
strings.filter_modded: True,
|
|
||||||
}
|
|
||||||
|
|
||||||
self.controller = controller
|
|
||||||
self.controller.register_widget("filters", self)
|
|
||||||
self.emitter = self.controller.get_emitter()
|
|
||||||
|
|
||||||
self.map_store = self.controller.get_map_store()
|
|
||||||
|
|
||||||
self.checks = []
|
|
||||||
self.maps_hr = []
|
|
||||||
|
|
||||||
self.enabled_filters = dict(self.default_filters)
|
|
||||||
self.keyword_filter = ""
|
|
||||||
#self.selected_map: str = strings.all_maps
|
|
||||||
#self.prior_map: str = strings.all_maps
|
|
||||||
|
|
||||||
self.button_grid = Gtk.Grid(
|
|
||||||
halign=Gtk.Align.CENTER, column_spacing=5, column_homogeneous=True
|
halign=Gtk.Align.CENTER, column_spacing=5, column_homogeneous=True
|
||||||
)
|
)
|
||||||
row = 1
|
row = 1
|
||||||
col = 0
|
col = 0
|
||||||
|
|
||||||
|
self.controller = controller
|
||||||
|
self.emitter = controller.get_emitter()
|
||||||
|
|
||||||
|
self.checks: list[Gtk.CheckButton] = []
|
||||||
|
|
||||||
# TODO: use enumerated checks
|
# TODO: use enumerated checks
|
||||||
for check in self.default_filters.keys():
|
for check in defaults.keys():
|
||||||
checkbox = Gtk.CheckButton(label=check)
|
checkbox = Gtk.CheckButton(label=check)
|
||||||
label = checkbox.get_children()
|
label = checkbox.get_children()
|
||||||
label[0].set_ellipsize(Pango.EllipsizeMode.END)
|
label[0].set_ellipsize(Pango.EllipsizeMode.END)
|
||||||
|
|
||||||
if self.default_filters[check]:
|
if defaults[check]:
|
||||||
checkbox.set_active(True)
|
checkbox.set_active(True)
|
||||||
|
|
||||||
col = col + 1
|
col = col + 1
|
||||||
if col > 3:
|
if col > 3:
|
||||||
row += 1
|
row += 1
|
||||||
col = 1
|
col = 1
|
||||||
self.button_grid.attach(checkbox, col, row, 1, 1)
|
self.attach(checkbox, col, row, 1, 1)
|
||||||
|
|
||||||
checkbox.connect("toggled", self._on_check_toggled)
|
checkbox.connect("toggled", self._on_check_toggled)
|
||||||
self.checks.append(checkbox)
|
self.checks.append(checkbox)
|
||||||
|
|
||||||
self.connect("button-release-event", self._on_button_release)
|
def block_toggles(self, state: bool) -> None:
|
||||||
self.set_orientation(Gtk.Orientation.VERTICAL)
|
for check in self.checks:
|
||||||
|
self.controller.suppress_signal(
|
||||||
|
self,
|
||||||
|
check,
|
||||||
|
"_on_check_toggled",
|
||||||
|
state,
|
||||||
|
)
|
||||||
|
|
||||||
|
def reload_filters(self) -> None:
|
||||||
|
checkboxes = self.checks
|
||||||
|
self.block_toggles(True)
|
||||||
|
filters = self.controller.get_enabled_filters()
|
||||||
|
for check in checkboxes:
|
||||||
|
label = check.get_label()
|
||||||
|
state = filters[label]
|
||||||
|
check.set_active(state)
|
||||||
|
self.block_toggles(False)
|
||||||
|
|
||||||
|
def _on_check_toggled(self, button: Gtk.CheckButton) -> None:
|
||||||
|
label = button.get_label()
|
||||||
|
state = button.get_active()
|
||||||
|
logger.info(f"User toggled button '{label}' to {state}")
|
||||||
|
self.emitter.emit("check_toggled", label, state)
|
||||||
|
|
||||||
|
def get_checkboxes(self) -> list:
|
||||||
|
return self.checks
|
||||||
|
|
||||||
|
|
||||||
|
class KeywordEntry(Gtk.Entry):
|
||||||
|
def __init__(self, controller: "Controller") -> None:
|
||||||
|
# TODO :strings
|
||||||
|
super().__init__(placeholder_text="Filter by keyword")
|
||||||
|
|
||||||
|
self.keyword_filter = ""
|
||||||
|
self.controller = controller
|
||||||
|
self.connect("activate", self._on_activated)
|
||||||
|
self.connect("key-press-event", self._on_keypress)
|
||||||
|
self.connect("icon-release", lambda *args: self.activate())
|
||||||
|
|
||||||
|
self.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, SEARCH_ICON)
|
||||||
|
self.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, True)
|
||||||
|
|
||||||
|
def get_keyword(self) -> str:
|
||||||
|
return self.keyword_filter
|
||||||
|
|
||||||
|
def _on_keypress(self, entry: Gtk.Entry, event: Gdk.EventKey) -> bool:
|
||||||
|
match event.keyval:
|
||||||
|
case Gdk.KEY_Up:
|
||||||
|
return True
|
||||||
|
case Gdk.KEY_Down:
|
||||||
|
return True
|
||||||
|
case Gdk.KEY_Escape:
|
||||||
|
GLib.idle_add(self.restore_focus_to_treeview)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _on_activated(self, entry: Gtk.Entry) -> None:
|
||||||
|
# TODO: investigate this method
|
||||||
|
self.controller.mediator.window.set_keep_below(False)
|
||||||
|
keyword = entry.get_text().lower()
|
||||||
|
if keyword == self.keyword_filter:
|
||||||
|
return
|
||||||
|
if keyword.isspace():
|
||||||
|
return
|
||||||
|
logger.info(f"User filtered by keyword '{keyword}'")
|
||||||
|
self.keyword_filter = keyword
|
||||||
|
self.controller.refilter_model(FilterMode.KEYWORD, keyword)
|
||||||
|
|
||||||
|
|
||||||
|
class FilterPanel(Gtk.Box):
|
||||||
|
def __init__(self, controller: "Controller") -> None:
|
||||||
|
super().__init__(spacing=6, vexpand=False, orientation=Gtk.Orientation.VERTICAL)
|
||||||
|
|
||||||
|
# TODO: get these from metamanager
|
||||||
|
# self.default_filters = {
|
||||||
|
# strings.filter_1pp: True,
|
||||||
|
# strings.filter_day: True,
|
||||||
|
# strings.filter_empty: False,
|
||||||
|
# strings.filter_3pp: True,
|
||||||
|
# strings.filter_night: True,
|
||||||
|
# strings.filter_full: False,
|
||||||
|
# strings.filter_lowpop: True,
|
||||||
|
# strings.filter_nonascii: False,
|
||||||
|
# strings.filter_duplicate: False,
|
||||||
|
# strings.filter_official: True,
|
||||||
|
# strings.filter_unofficial: True,
|
||||||
|
# strings.filter_modded: True,
|
||||||
|
# }
|
||||||
|
|
||||||
|
self.controller = controller
|
||||||
|
self.controller.register_widget("filters", self)
|
||||||
|
self.emitter = self.controller.get_emitter()
|
||||||
|
|
||||||
|
# self.selected_map: str = strings.all_maps
|
||||||
|
# self.prior_map: str = strings.all_maps
|
||||||
|
# self.set_orientation(Gtk.Orientation.VERTICAL)
|
||||||
|
|
||||||
|
# TODO: initialize to empty
|
||||||
|
map_man = self.controller.get_map_man()
|
||||||
|
defaults = map_man.get_default_filters()
|
||||||
|
self.map_store = map_man.get_map_store()
|
||||||
|
self.enabled_filters = defaults
|
||||||
|
|
||||||
|
self.keyword_entry = KeywordEntry(self.controller)
|
||||||
|
self.button_grid = ButtonGrid(self.controller, defaults)
|
||||||
|
|
||||||
|
# TODO: unintended legacy behavior?
|
||||||
|
self.connect("button-release-event", lambda *args: True)
|
||||||
set_surrounding_margins(self, 10)
|
set_surrounding_margins(self, 10)
|
||||||
self.set_margin_top(1)
|
self.set_margin_top(1)
|
||||||
|
|
||||||
# TODO: strings
|
# TODO: strings
|
||||||
self.filters_label = BoldLabel("Filters")
|
self.filters_label = BoldLabel("Filters")
|
||||||
|
|
||||||
self.keyword_entry = Gtk.Entry()
|
|
||||||
# TODO :strings
|
|
||||||
self.keyword_entry.set_placeholder_text("Filter by keyword")
|
|
||||||
self.keyword_entry.connect("activate", self._on_keyword_activated)
|
|
||||||
self.keyword_entry.connect("key-press-event", self._on_keyword_keypress)
|
|
||||||
self.keyword_entry.set_icon_from_icon_name(
|
|
||||||
Gtk.EntryIconPosition.SECONDARY, SEARCH_ICON
|
|
||||||
)
|
|
||||||
self.keyword_entry.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, True)
|
|
||||||
self.keyword_entry.connect(
|
|
||||||
"icon-release", lambda *args: self.keyword_entry.activate()
|
|
||||||
)
|
|
||||||
|
|
||||||
self.emitter.connect(
|
self.emitter.connect(
|
||||||
"request_keyword_focus", lambda _: self.keyword_entry.grab_focus()
|
"request_keyword_focus", lambda _: self.keyword_entry.grab_focus()
|
||||||
)
|
)
|
||||||
@@ -107,6 +175,7 @@ class FilterPanel(Gtk.Box):
|
|||||||
self.emitter.connect("servers_loaded", self._on_servers_loaded)
|
self.emitter.connect("servers_loaded", self._on_servers_loaded)
|
||||||
self.emitter.connect("load_maps", self._on_maps_loaded)
|
self.emitter.connect("load_maps", self._on_maps_loaded)
|
||||||
|
|
||||||
|
# TODO: break into MapsCombo class
|
||||||
completion = Gtk.EntryCompletion(inline_completion=True)
|
completion = Gtk.EntryCompletion(inline_completion=True)
|
||||||
completion.set_text_column(0)
|
completion.set_text_column(0)
|
||||||
completion.set_minimum_key_length(1)
|
completion.set_minimum_key_length(1)
|
||||||
@@ -121,14 +190,30 @@ class FilterPanel(Gtk.Box):
|
|||||||
self.maps_entry.set_placeholder_text("Filter by map")
|
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_combo.pack_start(renderer_text, EXPAND)
|
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)
|
||||||
|
|
||||||
for el in self.filters_label, self.keyword_entry, self.maps_combo, self.button_grid:
|
for el in (
|
||||||
|
self.filters_label,
|
||||||
|
self.keyword_entry,
|
||||||
|
self.maps_combo,
|
||||||
|
self.button_grid,
|
||||||
|
):
|
||||||
self.pack_start(el, NO_EXPAND, NO_FILL, NO_PADDING)
|
self.pack_start(el, NO_EXPAND, NO_FILL, NO_PADDING)
|
||||||
|
|
||||||
|
def _on_maps_activated(self, entry: Gtk.Entry) -> None:
|
||||||
|
text = entry.get_text()
|
||||||
|
model = self.maps_combo.get_model()
|
||||||
|
if text is None:
|
||||||
|
return
|
||||||
|
for i, row in enumerate(model):
|
||||||
|
if text == row[0]:
|
||||||
|
self.maps_combo.set_active(i)
|
||||||
|
self._on_map_changed(self.maps_combo)
|
||||||
|
|
||||||
def _on_servers_loaded(self, emitter: "Emitter", tab: "ServerTab") -> None:
|
def _on_servers_loaded(self, emitter: "Emitter", tab: "ServerTab") -> None:
|
||||||
state = self.controller.has_server_model()
|
state = self.controller.has_server_model()
|
||||||
for widget in (self.keyword_entry, self.maps_combo, self.button_grid):
|
for widget in (self.keyword_entry, self.maps_combo, self.button_grid):
|
||||||
@@ -137,17 +222,23 @@ class FilterPanel(Gtk.Box):
|
|||||||
def _on_maps_loaded(self, emitter: "Emitter", store: Gtk.ListStore) -> None:
|
def _on_maps_loaded(self, emitter: "Emitter", store: Gtk.ListStore) -> None:
|
||||||
self.maps_combo.set_model(store)
|
self.maps_combo.set_model(store)
|
||||||
self.maps_combo.set_active(0)
|
self.maps_combo.set_active(0)
|
||||||
|
self.button_grid.reload_filters()
|
||||||
|
|
||||||
|
# TODO: move into metamanager
|
||||||
def get_filters(self) -> tuple:
|
def get_filters(self) -> tuple:
|
||||||
selected = self.controller.get_selected_map()
|
selected = self.controller.get_selected_map()
|
||||||
|
enabled = self.controller.get_enabled_filters()
|
||||||
filters = []
|
filters = []
|
||||||
filters.append(selected)
|
filters.append(selected)
|
||||||
filters.append(self.keyword_filter)
|
filters.append(self.get_keyword_filter())
|
||||||
for k in self.enabled_filters:
|
for filt in enabled:
|
||||||
if not self.enabled_filters[k]:
|
if enabled[filt] is False:
|
||||||
filters.append(k)
|
filters.append(filt)
|
||||||
return tuple(filters)
|
return tuple(filters)
|
||||||
|
|
||||||
|
def get_keyword_filter(self) -> str:
|
||||||
|
return self.keyword_entry.get_keyword()
|
||||||
|
|
||||||
# TODO: should be used when switching ServerTab contexts
|
# TODO: should be used when switching ServerTab contexts
|
||||||
# use signals here
|
# use signals here
|
||||||
# def reinit_panel(self) -> None:
|
# def reinit_panel(self) -> None:
|
||||||
@@ -159,29 +250,17 @@ class FilterPanel(Gtk.Box):
|
|||||||
# if sel_panel.is_visible():
|
# if sel_panel.is_visible():
|
||||||
# sel_panel.set_visible(False)
|
# sel_panel.set_visible(False)
|
||||||
|
|
||||||
# TODO: change filters on a per-tab basis
|
# TODO: this chiefly applies when clicking refresh button, etc.
|
||||||
def reinit_filters(self) -> None:
|
# and setting filters to default state
|
||||||
self.enabled_filters = dict(self.default_filters)
|
# def reinit_filters(self) -> None:
|
||||||
for check in self.checks:
|
# self.enabled_filters = dict(self.default_filters)
|
||||||
label = check.get_label()
|
# for check in self.checks:
|
||||||
state = self.default_filters[label]
|
# label = check.get_label()
|
||||||
check.set_active(state)
|
# state = self.default_filters[label]
|
||||||
|
# check.set_active(state)
|
||||||
|
|
||||||
def _on_map_entry_keypress(self, entry: Gtk.Entry, event: Gdk.EventKey) -> None:
|
def _on_map_entry_keypress(self, entry: Gtk.Entry, event: Gdk.EventKey) -> None:
|
||||||
# TODO: use activated() signal
|
|
||||||
match event.keyval:
|
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(self.map_store): # type: ignore
|
|
||||||
if text == i[1][0]:
|
|
||||||
self.maps_combo.set_active(i[0])
|
|
||||||
self._on_map_changed(self.maps_combo)
|
|
||||||
case Gdk.KEY_Escape:
|
case Gdk.KEY_Escape:
|
||||||
GLib.idle_add(self.restore_focus_to_treeview)
|
GLib.idle_add(self.restore_focus_to_treeview)
|
||||||
"""
|
"""
|
||||||
@@ -191,7 +270,7 @@ class FilterPanel(Gtk.Box):
|
|||||||
text = self.maps_entry.get_text()
|
text = self.maps_entry.get_text()
|
||||||
self.maps_entry.set_position(len(text))
|
self.maps_entry.set_position(len(text))
|
||||||
case _:
|
case _:
|
||||||
return
|
return False
|
||||||
|
|
||||||
def _on_completer_match(
|
def _on_completer_match(
|
||||||
self,
|
self,
|
||||||
@@ -213,17 +292,6 @@ class FilterPanel(Gtk.Box):
|
|||||||
view.grab_focus()
|
view.grab_focus()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _on_keyword_keypress(self, entry: Gtk.Entry, event: Gdk.EventKey) -> bool:
|
|
||||||
match event.keyval:
|
|
||||||
case Gdk.KEY_Up:
|
|
||||||
return True
|
|
||||||
case Gdk.KEY_Down:
|
|
||||||
return True
|
|
||||||
case Gdk.KEY_Escape:
|
|
||||||
GLib.idle_add(self.restore_focus_to_treeview)
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _on_combo_keypress(self, combo: Gtk.ComboBox, event: Gdk.EventKey) -> bool:
|
def _on_combo_keypress(self, combo: Gtk.ComboBox, event: Gdk.EventKey) -> bool:
|
||||||
match event.keyval:
|
match event.keyval:
|
||||||
case Gdk.KEY_Down:
|
case Gdk.KEY_Down:
|
||||||
@@ -232,33 +300,13 @@ class FilterPanel(Gtk.Box):
|
|||||||
case _:
|
case _:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_keyword_filter(self) -> str:
|
|
||||||
return self.keyword_filter
|
|
||||||
|
|
||||||
def _on_keyword_activated(self, entry: Gtk.Entry) -> None:
|
|
||||||
# TODO:
|
|
||||||
self.controller.mediator.window.set_keep_below(False)
|
|
||||||
keyword = entry.get_text().lower()
|
|
||||||
if keyword == self.keyword_filter:
|
|
||||||
return
|
|
||||||
if keyword.isspace():
|
|
||||||
return
|
|
||||||
logger.info(f"User filtered by keyword '{keyword}'")
|
|
||||||
self.keyword_filter = keyword
|
|
||||||
self.controller.refilter_model(FilterMode.KEYWORD, keyword)
|
|
||||||
|
|
||||||
def _on_button_release(self, window, button) -> Literal[True]:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def get_active_combo(self) -> int:
|
def get_active_combo(self) -> int:
|
||||||
return self.maps_combo.get_active()
|
return self.maps_combo.get_active()
|
||||||
|
|
||||||
def set_active_combo(self, row: int) -> None:
|
def set_active_combo(self, row: int) -> None:
|
||||||
self.maps_combo.set_active(row)
|
self.maps_combo.set_active(row)
|
||||||
|
|
||||||
def toggle_check_by_key(
|
def toggle_check_by_key(self, emitter: "Emitter", keyval: int) -> bool:
|
||||||
self, emitter: "Emitter", keyval: int
|
|
||||||
) -> None | Literal[False]:
|
|
||||||
mappings = {
|
mappings = {
|
||||||
Gdk.KEY_1: 0,
|
Gdk.KEY_1: 0,
|
||||||
Gdk.KEY_2: 1,
|
Gdk.KEY_2: 1,
|
||||||
@@ -277,24 +325,14 @@ class FilterPanel(Gtk.Box):
|
|||||||
return False
|
return False
|
||||||
index = mappings[keyval]
|
index = mappings[keyval]
|
||||||
self.toggle_check(index)
|
self.toggle_check(index)
|
||||||
|
return True
|
||||||
|
|
||||||
def toggle_check(self, digit: int) -> None:
|
def toggle_check(self, digit: int) -> None:
|
||||||
check = self.checks[digit]
|
checks = self.button_grid.get_checkboxes()
|
||||||
|
check = checks[digit]
|
||||||
state = check.get_active()
|
state = check.get_active()
|
||||||
check.set_active(not state)
|
check.set_active(not state)
|
||||||
|
|
||||||
def _on_check_toggled(self, button: Gtk.CheckButton) -> None:
|
|
||||||
label = button.get_label()
|
|
||||||
state = button.get_active()
|
|
||||||
logger.info(f"User toggled button '{label}' to {state}")
|
|
||||||
if state:
|
|
||||||
mode = FilterMode.TOGGLE_ON
|
|
||||||
else:
|
|
||||||
mode = FilterMode.TOGGLE_OFF
|
|
||||||
|
|
||||||
self.enabled_filters[label] = state
|
|
||||||
self.controller.refilter_model(mode, label)
|
|
||||||
|
|
||||||
def _on_map_changed(self, combo: Gtk.ComboBox) -> None:
|
def _on_map_changed(self, combo: Gtk.ComboBox) -> None:
|
||||||
old_sel = self.controller.get_selected_map()
|
old_sel = self.controller.get_selected_map()
|
||||||
model = combo.get_model()
|
model = combo.get_model()
|
||||||
@@ -306,6 +344,6 @@ class FilterPanel(Gtk.Box):
|
|||||||
return
|
return
|
||||||
if not selection:
|
if not selection:
|
||||||
return
|
return
|
||||||
logger.info(f"User selected map '{selection}'")
|
|
||||||
self.maps_entry.set_text(selection)
|
self.maps_entry.set_text(selection)
|
||||||
|
logger.info(f"User selected map '{selection}'")
|
||||||
self.emitter.emit("map_selection_changed", selection)
|
self.emitter.emit("map_selection_changed", selection)
|
||||||
@@ -4,6 +4,7 @@ from typing import Self, TYPE_CHECKING
|
|||||||
from dzgui.const.enum import Popup, Preferences
|
from dzgui.const.enum import Popup, Preferences
|
||||||
from dzgui.const.constants import NO_EXPAND, NO_FILL
|
from dzgui.const.constants import NO_EXPAND, NO_FILL
|
||||||
from dzgui.util.open_links import open_user_workshop
|
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.dialogs.generic import GenericDialog
|
||||||
from dzgui.views.components.buttons import SteamTextButton, WebButton
|
from dzgui.views.components.buttons import SteamTextButton, WebButton
|
||||||
|
|
||||||
@@ -18,9 +19,9 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
class WorkshopLinkDialog(GenericDialog):
|
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):
|
||||||
super().__init__(controller, text, Popup.NOTIFY)
|
text = textwrap.dedent(text).replace("\n", " ")
|
||||||
|
super().__init__(controller, text=notice, mtype=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK, secondary=text)
|
||||||
text = textwrap.dedent(text)
|
self.controller = controller
|
||||||
self.dialogBox = self.get_content_area()
|
self.dialogBox = self.get_content_area()
|
||||||
self.set_default_response(Gtk.ResponseType.OK)
|
self.set_default_response(Gtk.ResponseType.OK)
|
||||||
self.set_size_request(500, 0)
|
self.set_size_request(500, 0)
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ class ServerTreeView(ContextMixin, TreeView):
|
|||||||
self.emitter.connect("servers_loaded", self._on_servers_loaded)
|
self.emitter.connect("servers_loaded", self._on_servers_loaded)
|
||||||
|
|
||||||
def _on_servers_loaded(self, emitter: "Emitter", tab: "ServerTab") -> None:
|
def _on_servers_loaded(self, emitter: "Emitter", tab: "ServerTab") -> None:
|
||||||
|
# FIXME: gets called on every tab, should be localized only to that context
|
||||||
state = self.controller.has_server_model()
|
state = self.controller.has_server_model()
|
||||||
self.set_headers_clickable(state)
|
self.set_headers_clickable(state)
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user