mirror of
https://github.com/aclist/dztui.git
synced 2026-08-26 17:57:06 +02:00
feat: launch offline (WIP)
This commit is contained in:
parent
b17b8c24ff
commit
e6026e84f9
@ -54,7 +54,7 @@ if TYPE_CHECKING:
|
||||
from dzgui.views.pages.servers import ServerNotebook
|
||||
from dzgui.views.trees.tree_log import LogTreeView
|
||||
from dzgui.views.trees.tree_menu import MenuTreeView
|
||||
from dzgui.views.trees.tree_mods import ModTreeView
|
||||
from dzgui.views.trees.tree_mods import ModTreeView, OfflineModTreeView
|
||||
from dzgui.views.trees.tree_servers import ServerTreeView
|
||||
|
||||
|
||||
@ -221,19 +221,29 @@ class Controller(GObject.GObject):
|
||||
open_user_workshop(uid, client)
|
||||
|
||||
def load_mods(self) -> None:
|
||||
self.mod_man = ModManager(self)
|
||||
self.mediator.modtreeview.load_mods()
|
||||
|
||||
def uncolorize_mods(self) -> None:
|
||||
self.mod_man.uncolorize_mods()
|
||||
mod_man = self.mediator.modtreeview.get_mod_man()
|
||||
mod_man.uncolorize_mods()
|
||||
|
||||
def highlight_stale(self) -> None:
|
||||
self.mod_man.highlight_stale()
|
||||
mod_man = self.mediator.modtreeview.get_mod_man()
|
||||
mod_man.highlight_stale()
|
||||
|
||||
def toggle_mod_selection(self, state: bool) -> None:
|
||||
self.mod_man.toggle_mod_selection(state)
|
||||
mod_man = self.mediator.modtreeview.get_mod_man()
|
||||
mod_man.toggle_mod_selection(state)
|
||||
|
||||
def delete_mods(self) -> None:
|
||||
self.mod_man.delete_mods()
|
||||
def delete_mods(
|
||||
self, treeview: Union["ModTreeView", "OfflineModTreeView"] = None
|
||||
) -> None:
|
||||
if treeview is None:
|
||||
view = self.mediator.modtreeview
|
||||
else:
|
||||
view = treeview
|
||||
mod_man = view.get_mod_man()
|
||||
mod_man.delete_mods()
|
||||
|
||||
def get_mod_store(self) -> Gtk.TreeModel | None:
|
||||
return self.mediator.modtreeview.get_model()
|
||||
@ -260,7 +270,8 @@ class Controller(GObject.GObject):
|
||||
dialog.run()
|
||||
|
||||
def select_colorized(self) -> None:
|
||||
self.mod_man.select_colorized()
|
||||
mod_man = self.mediator.modtreeview.get_mod_man()
|
||||
mod_man.select_colorized()
|
||||
|
||||
def get_all_tree_filters(self) -> dict[str, dict[str, bool]]:
|
||||
servers = self.get_servers()
|
||||
@ -293,7 +304,9 @@ class Controller(GObject.GObject):
|
||||
|
||||
def set_custom_folder(self) -> Union["Path", None]:
|
||||
picker = FolderPicker(self.mediator.window)
|
||||
return picker.pick_folder()
|
||||
folder = picker.pick_folder()
|
||||
picker.destroy()
|
||||
return folder
|
||||
|
||||
def update_api_key(self, key: Preferences, text: str) -> None:
|
||||
self.config_man.update_api_key(key, text)
|
||||
@ -541,6 +554,8 @@ class Controller(GObject.GObject):
|
||||
return True
|
||||
return False
|
||||
|
||||
def open_offline(self, mods: Gtk.TreeModel | None) -> None:
|
||||
def open_offline(self) -> None:
|
||||
self.open_page(NotebookPage.OFFLINE)
|
||||
self.mediator.offline_loader.populate(mods)
|
||||
mod_man = self.mediator.modtreeview.get_mod_man()
|
||||
store = mod_man.store
|
||||
self.mediator.offline_loader.populate(store)
|
||||
|
||||
@ -8,7 +8,7 @@ from dzgui.util.clip import copy_clipboard
|
||||
from dzgui.util.open_links import open_workshop_page
|
||||
|
||||
from dzgui.views.dialogs.note import NoteDialog
|
||||
from dzgui.views.trees.tree_mods import ModTreeView
|
||||
from dzgui.views.trees.tree_mods import ModTreeView, OfflineModTreeView
|
||||
from dzgui.views.trees.tree_log import LogTreeView
|
||||
from dzgui.views.trees.tree_servers import ServerTreeView
|
||||
from dzgui.views.trees.tree_server_mods import ServerModTreeView
|
||||
@ -27,7 +27,13 @@ if TYPE_CHECKING:
|
||||
class ContextMenuManager:
|
||||
def __init__(
|
||||
self,
|
||||
treeview: LogTreeView | ModTreeView | ServerTreeView | ServerModTreeView,
|
||||
treeview: (
|
||||
LogTreeView
|
||||
| ModTreeView
|
||||
| ServerTreeView
|
||||
| ServerModTreeView
|
||||
| OfflineModTreeView
|
||||
),
|
||||
controller: "Controller",
|
||||
) -> None:
|
||||
self.controller = controller
|
||||
@ -72,10 +78,10 @@ class ContextMenuManager:
|
||||
case ContextMenu.SHOW_MODS:
|
||||
self.controller.get_modlist(record)
|
||||
|
||||
if isinstance(self.treeview, ModTreeView):
|
||||
if isinstance(self.treeview, (ModTreeView, OfflineModTreeView)):
|
||||
match action:
|
||||
case ContextMenu.DELETE_MOD:
|
||||
self.controller.delete_mods()
|
||||
self.controller.delete_mods(self.treeview)
|
||||
case ContextMenu.OPEN_WORKSHOP:
|
||||
self.open_mod_page()
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import logging
|
||||
import shutil
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any, TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from dzgui.api.mods import (
|
||||
get_delimited_mods,
|
||||
@ -40,20 +40,20 @@ class ModManager:
|
||||
this manager is instantiated each time the Mods page is opened
|
||||
"""
|
||||
|
||||
def __init__(self, controller: "Controller") -> None:
|
||||
def __init__(self, tree: Gtk.TreeView, controller: "Controller") -> None:
|
||||
self.treeview = tree
|
||||
self.controller = controller
|
||||
|
||||
self.emitter = controller.get_emitter()
|
||||
self.prefs = controller.get_prefs()
|
||||
self.path = controller.query_config(Preferences.DEFAULT)
|
||||
self.treeview = self.controller.get_modtreeview()
|
||||
|
||||
self.store: FastInsertListStore
|
||||
|
||||
self.thread_man = ThreadingManager(controller)
|
||||
self._get_mods()
|
||||
|
||||
@call_on_thread(dialogs.fetching_mods)
|
||||
def _get_mods(self) -> None:
|
||||
def load_mods(self) -> None:
|
||||
mods = get_delimited_mods(self.path)
|
||||
if len(mods) < 1:
|
||||
msg = self.format_mod_statusbar()
|
||||
@ -66,8 +66,6 @@ class ModManager:
|
||||
func = StoredFunc(self._on_mods_loaded)
|
||||
self.thread_man.set_cleanup_func(func)
|
||||
|
||||
# def _on_mods_loaded(self, mods: list[list[Any]]) -> None:
|
||||
|
||||
def _on_mods_loaded(self) -> None:
|
||||
self.treeview.set_model(self.store)
|
||||
msg = self.format_mod_statusbar()
|
||||
|
||||
@ -165,3 +165,6 @@ class ModelFactory:
|
||||
|
||||
def make_server_store(self) -> FastInsertListStore:
|
||||
return self.new_model_from_class(ServerCols)
|
||||
|
||||
def convert_model_to_list(self, model: "FastInsertListStore") -> list:
|
||||
return [[el for el in row] for row in model]
|
||||
|
||||
@ -1 +1,11 @@
|
||||
heading = "Play offline"
|
||||
|
||||
mission_button = "Select mission folder"
|
||||
mission_frame = "Mission"
|
||||
|
||||
version = "DayZ Version"
|
||||
|
||||
custom_frame = "Custom mods"
|
||||
custom_button = "Set custom mod folder"
|
||||
|
||||
local_frame = "Installed mods"
|
||||
|
||||
@ -64,7 +64,7 @@ connect = "Connect"
|
||||
|
||||
# Columns
|
||||
server_mod_cols = ["Mod", "ID", "Installed"]
|
||||
mod_cols = ["Mod", "Symlink", "Dir", "Size (MiB)", "Color"]
|
||||
mod_cols = ["Mod", "Symlink", "ID", "Size (MiB)", "Color"]
|
||||
log_cols = ["Timestamp", "Flag", "Traceback", "Message"]
|
||||
browser_cols = [
|
||||
"Name",
|
||||
|
||||
@ -23,6 +23,7 @@ from dzgui.views.pages.help import Help
|
||||
from dzgui.views.pages.keys import Keybindings
|
||||
from dzgui.views.pages.log import Log
|
||||
from dzgui.views.pages.mods import Mods
|
||||
|
||||
from dzgui.views.pages.offline import OfflineLoader
|
||||
from dzgui.views.pages.options import Options
|
||||
from dzgui.views.pages.preconnect import PreConnectionAssistant
|
||||
|
||||
@ -6,16 +6,23 @@ gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk # noqa
|
||||
|
||||
|
||||
# TODO: subclass Gtk.Frame directly
|
||||
class HeadingFrame(Gtk.Box):
|
||||
def __init__(self, widget: Gtk.Widget, heading: str) -> None:
|
||||
def __init__(self, heading: str = "") -> None:
|
||||
super().__init__(orientation=Gtk.Orientation.VERTICAL)
|
||||
|
||||
label = Gtk.Label(label=heading)
|
||||
label.set_halign(Gtk.Align.START)
|
||||
css.add_class(label, "settings-subheading")
|
||||
self.label = Gtk.Label(label=heading)
|
||||
self.label.set_halign(Gtk.Align.START)
|
||||
css.add_class(self.label, "settings-subheading")
|
||||
|
||||
frame = Gtk.Frame(hexpand=True)
|
||||
frame.add(widget)
|
||||
frame.set_label_widget(label)
|
||||
self.frame = Gtk.Frame(hexpand=True)
|
||||
self.frame.set_label_widget(self.label)
|
||||
|
||||
self.add(frame)
|
||||
self.add(self.frame)
|
||||
|
||||
@classmethod
|
||||
def new_with_widget_and_label(cls, widget: Gtk.Widget, label: str) -> None:
|
||||
n = cls()
|
||||
n.frame.add(widget)
|
||||
n.label.set_label(label)
|
||||
return n
|
||||
|
||||
@ -44,6 +44,8 @@ class ContextMixin(TreeView):
|
||||
self.context_menu.connect("key-press-event", self._on_key)
|
||||
|
||||
for row in group.value:
|
||||
if row is None:
|
||||
return
|
||||
item = self._process_dynamic_row(row)
|
||||
self.context_menu.append(item)
|
||||
|
||||
|
||||
@ -11,12 +11,14 @@ if TYPE_CHECKING:
|
||||
from dzgui.controllers.mc import Controller
|
||||
|
||||
|
||||
class Mods(NoOverlayScrolledWindow):
|
||||
class Mods(Gtk.Box):
|
||||
def __init__(self, controller: "Controller") -> None:
|
||||
super().__init__()
|
||||
super().__init__(orientation=Gtk.Orientation.VERTICAL)
|
||||
|
||||
self.box = NoOverlayScrolledWindow()
|
||||
self.tree = ModTreeView(controller)
|
||||
self.tree.set_vexpand(True)
|
||||
self.box.add(self.tree)
|
||||
|
||||
self.controller = controller
|
||||
self.controller.register_widget("modtreeview", self.tree)
|
||||
@ -30,18 +32,14 @@ class Mods(NoOverlayScrolledWindow):
|
||||
)
|
||||
self.offline_button.connect("clicked", self._on_offline_clicked)
|
||||
|
||||
self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
self.box.add(self.offline_button)
|
||||
self.box.add(self.tree)
|
||||
|
||||
self.add(self.offline_button)
|
||||
self.add(self.box)
|
||||
|
||||
self.connect("map", self._on_map)
|
||||
self.connect("unmap", self._on_unmap)
|
||||
|
||||
def _on_offline_clicked(self, button: Gtk.Button) -> None:
|
||||
self.controller.open_offline(self.tree.get_model())
|
||||
# TODO: add offline page
|
||||
self.controller.open_offline()
|
||||
|
||||
def _on_unmap(self, widget: Self) -> None:
|
||||
self.emitter.emit("mod_page_toggled", False)
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
from typing import Self, TYPE_CHECKING
|
||||
from pathlib import Path
|
||||
from typing import Self, Sequence, TYPE_CHECKING
|
||||
|
||||
from dzgui.strings import offline
|
||||
from dzgui.util import css
|
||||
import dzgui.api.pefile as PeFile
|
||||
from dzgui.const.constants import APPID_DAYZ_EXP, APPNAME_DAYZ, APPNAME_DAYZ_EXP_HUMAN
|
||||
from dzgui.const.enum import NotebookPage, Preferences
|
||||
from dzgui.strings import offline
|
||||
from dzgui.views.components.scrollable import NoOverlayScrolledWindow
|
||||
from dzgui.views.components.frame import HeadingFrame
|
||||
from dzgui.views.trees.tree_mods import ModTreeView
|
||||
from dzgui.views.trees.tree_mods import OfflineModTreeView
|
||||
|
||||
|
||||
import gi
|
||||
@ -13,9 +18,144 @@ from gi.repository import Gtk, Gdk # noqa
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from dzgui.controllers.mc import Controller
|
||||
from dzgui.model.model_factory import FastInsertListStore
|
||||
|
||||
# FIXME: grab focus of outer page
|
||||
# TODO: if any custom mods, symlink them before connecting proceeding
|
||||
|
||||
class GenericBox(Gtk.Box):
|
||||
def __init__(self, orientation: Gtk.Orientation) -> None:
|
||||
super().__init__(orientation=orientation)
|
||||
|
||||
def extend(self, els: Sequence[Gtk.Widget]) -> None:
|
||||
for el in els:
|
||||
self.add(el)
|
||||
|
||||
|
||||
class HBox(GenericBox):
|
||||
def __init__(self) -> None:
|
||||
super().__init__(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
|
||||
|
||||
class VBox(GenericBox):
|
||||
def __init__(self) -> None:
|
||||
super().__init__(orientation=Gtk.Orientation.VERTICAL)
|
||||
|
||||
|
||||
class PageHeading(Gtk.Label):
|
||||
def __init__(self, label: str) -> None:
|
||||
super().__init__(label=label, halign=Gtk.Align.CENTER)
|
||||
|
||||
css.add_class(self, "page-heading")
|
||||
|
||||
|
||||
class FolderHBox(HBox):
|
||||
def __init__(self, btn_label: str) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.set_margin_start(10)
|
||||
self.set_margin_bottom(10)
|
||||
self.set_spacing(5)
|
||||
|
||||
# TODO: use IconButton with folder-symbolic
|
||||
self.button = Gtk.Button(label=btn_label, halign=Gtk.Align.START)
|
||||
self.label = Gtk.Label()
|
||||
self.extend([self.button, self.label])
|
||||
|
||||
def get_button(self) -> Gtk.Button:
|
||||
return self.button
|
||||
|
||||
def set_label(self, label: str) -> None:
|
||||
self.label.set_label(label)
|
||||
|
||||
|
||||
class ModFrame(HeadingFrame):
|
||||
def __init__(self, controller: "Controller", label: str) -> None:
|
||||
super().__init__(heading=label)
|
||||
|
||||
self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
|
||||
self.tree = OfflineModTreeView(controller)
|
||||
self.tree.set_model(None)
|
||||
|
||||
self.scrolled = NoOverlayScrolledWindow()
|
||||
self.scrolled.set_size_request(600, 400)
|
||||
self.scrolled.add(self.tree)
|
||||
self.status = Gtk.Label(
|
||||
halign=Gtk.Align.START, margin_start=5, margin_top=3, margin_bottom=3
|
||||
)
|
||||
|
||||
self.vbox.pack_end(self.status, expand=False, fill=False, padding=0)
|
||||
self.vbox.pack_end(self.scrolled, expand=False, fill=False, padding=0)
|
||||
self.frame.add(self.vbox)
|
||||
|
||||
sel = self.tree.get_selection()
|
||||
sel.connect("changed", self._on_selection_changed)
|
||||
|
||||
def collapse_tree(self) -> None:
|
||||
self.scrolled.hide()
|
||||
|
||||
def get_tree(self) -> OfflineModTreeView:
|
||||
return self.tree
|
||||
|
||||
def pack_start(self, widget: Gtk.Widget) -> None:
|
||||
self.vbox.pack_start(widget, expand=False, fill=False, padding=5)
|
||||
|
||||
def set_model(self, model: "FastInsertListStore") -> None:
|
||||
self.tree.set_model(model)
|
||||
self.tree.mod_man.store = model
|
||||
self.set_cursor()
|
||||
|
||||
def _on_selection_changed(self, sel: Gtk.TreeSelection) -> None:
|
||||
model, rows = sel.get_selected_rows()
|
||||
status = f"Mods selected: {len(rows)}"
|
||||
self.status.set_label(status)
|
||||
|
||||
def set_cursor(self) -> None:
|
||||
path = Gtk.TreePath.new_from_indices([0])
|
||||
self.tree.set_cursor(path)
|
||||
|
||||
|
||||
class CustomModFrame(ModFrame):
|
||||
def __init__(self, controller: "Controller", heading: str) -> None:
|
||||
super().__init__(controller, heading)
|
||||
|
||||
self.controller = controller
|
||||
self.custom_hbox = FolderHBox(offline.custom_button)
|
||||
self.custom_hbox.get_button().connect("clicked", self._on_custom_button_clicked)
|
||||
|
||||
self.pack_start(self.custom_hbox)
|
||||
|
||||
def _on_custom_button_clicked(self, button: Gtk.Button) -> None:
|
||||
# TODO: recycle for mission folder
|
||||
# TODO: propagate results back to parent
|
||||
folder = self.controller.set_custom_folder()
|
||||
if folder is not None:
|
||||
# TODO: CustomModManager
|
||||
self.custom_hbox.set_label(str(folder))
|
||||
|
||||
|
||||
class RadioFrame(HeadingFrame):
|
||||
def __init__(self, controller: "Controller") -> None:
|
||||
super().__init__(heading=offline.version)
|
||||
|
||||
self.controller = controller
|
||||
|
||||
self.dayz = Gtk.RadioButton.new_with_label(None, APPNAME_DAYZ)
|
||||
self.dayz_exp = Gtk.RadioButton.new_with_label_from_widget(
|
||||
self.dayz, APPNAME_DAYZ_EXP_HUMAN
|
||||
)
|
||||
|
||||
self.radio_box = HBox()
|
||||
self.radio_box.extend([self.dayz, self.dayz_exp])
|
||||
self.radio_box.set_margin_start(10)
|
||||
self.radio_box.set_margin_bottom(5)
|
||||
|
||||
self.frame.add(self.radio_box)
|
||||
|
||||
default_steam_path = self.controller.query_config(Preferences.DEFAULT)
|
||||
steam_path = Path(default_steam_path)
|
||||
dayz_exp = PeFile.get_pretty_version(steam_path, APPID_DAYZ_EXP)
|
||||
if dayz_exp is None:
|
||||
self.dayz_exp.set_sensitive(False)
|
||||
|
||||
|
||||
class OfflineLoader(Gtk.Box):
|
||||
@ -26,127 +166,80 @@ class OfflineLoader(Gtk.Box):
|
||||
margin_end=10,
|
||||
)
|
||||
|
||||
# TODO: wrap entire page in scrollable
|
||||
# TODO: wrap entire page in scrollable, see preconnect dialog
|
||||
# TODO: spacing between inner and outer scrollbars
|
||||
self.controller = controller
|
||||
self.controller.register_widget("offline_loader", self)
|
||||
|
||||
label = Gtk.Label(label=offline.heading)
|
||||
label.set_halign(Gtk.Align.CENTER)
|
||||
css.add_class(label, "page-heading")
|
||||
self.add(label)
|
||||
self.add(PageHeading(offline.heading))
|
||||
|
||||
# TODO: wrap trees in scrollable
|
||||
# TODO: suppress trees if there are no mods
|
||||
self.local_mods = ModTreeView(controller)
|
||||
self.scr = Gtk.ScrolledWindow()
|
||||
self.scr.add(self.local_mods)
|
||||
self.scr.set_size_request(600, 400)
|
||||
# TODO: strings
|
||||
self.local_frame = HeadingFrame(self.scr, "Installed mods")
|
||||
|
||||
# TODO: update selected count statusbar when changed
|
||||
sel = self.local_mods.get_selection()
|
||||
sel.connect("changed", self._on_selection_changed)
|
||||
|
||||
# TODO: abstract into class
|
||||
# TODO: folder label
|
||||
# TODO: descriptive text here explaining how this area works
|
||||
# TODO: use statusbar msg from format.format_mods
|
||||
# TODO: strings
|
||||
self.custom_button = Gtk.Button(
|
||||
label="Set custom mod folder", halign=Gtk.Align.START
|
||||
self.local_frame = ModFrame(controller, offline.local_frame)
|
||||
self.custom_frame = CustomModFrame(controller, offline.custom_frame)
|
||||
|
||||
self.mission_hbox = FolderHBox(offline.mission_button)
|
||||
self.mission_frame = HeadingFrame.new_with_widget_and_label(
|
||||
self.mission_hbox, offline.mission_frame
|
||||
)
|
||||
self.custom_button.connect("clicked", self._on_custom_button_clicked)
|
||||
self.custom_mods = Gtk.TreeView()
|
||||
self.custom_mods_box = Gtk.Box(
|
||||
orientation=Gtk.Orientation.VERTICAL, margin_start=10, margin_bottom=10
|
||||
)
|
||||
self.custom_mods_box.add(self.custom_button)
|
||||
self.custom_mods_box.add(self.custom_mods)
|
||||
|
||||
# TODO: strings
|
||||
self.custom_frame = HeadingFrame(self.custom_mods_box, "Custom mods")
|
||||
|
||||
self.mission_box = Gtk.Box(
|
||||
orientation=Gtk.Orientation.HORIZONTAL, margin_start=10, margin_bottom=10
|
||||
)
|
||||
# TODO: strings
|
||||
self.mission_button = Gtk.Button(label="Select mission folder")
|
||||
self.mission_label = Gtk.Label()
|
||||
|
||||
self.mission_box.add(self.mission_button)
|
||||
self.mission_box.add(self.mission_label)
|
||||
|
||||
# TODO: strings
|
||||
self.mission_frame = HeadingFrame(self.mission_box, "Mission")
|
||||
|
||||
self.radio_box = Gtk.Box(
|
||||
orientation=Gtk.Orientation.HORIZONTAL, margin_start=10, margin_bottom=10
|
||||
)
|
||||
# TODO: strings
|
||||
# TODO: disable if not available, use PeFile
|
||||
self.dayz = Gtk.RadioButton.new_with_label(None, "DayZ")
|
||||
self.dayz_exp = Gtk.RadioButton.new_with_label_from_widget(
|
||||
self.dayz, "DayZ Experimental"
|
||||
)
|
||||
self.radio_box.add(self.dayz)
|
||||
self.radio_box.add(self.dayz_exp)
|
||||
self.radio_frame = HeadingFrame(self.radio_box, "DayZ Version")
|
||||
self.radio_frame = RadioFrame(controller)
|
||||
# HeadingFrame.new_with_widget_and_label(self.radio_box, offline.version)
|
||||
|
||||
# TODO: abstraction
|
||||
self.scrollable = Gtk.ScrolledWindow(
|
||||
vexpand=True, propagate_natural_height=True
|
||||
)
|
||||
# TODO: use same button anchoring logic as preconnect dialog
|
||||
self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
|
||||
self.box.add(self.local_frame)
|
||||
self.box.add(self.custom_frame)
|
||||
self.box.add(self.mission_frame)
|
||||
self.box.add(self.radio_frame)
|
||||
self.scrollable.add(self.box)
|
||||
self.content_box = VBox()
|
||||
self.content_box.set_spacing(5)
|
||||
self.content_box.extend(
|
||||
[
|
||||
self.local_frame,
|
||||
self.custom_frame,
|
||||
self.mission_frame,
|
||||
self.radio_frame,
|
||||
]
|
||||
)
|
||||
|
||||
self.scrollable.add(self.content_box)
|
||||
self.add(self.scrollable)
|
||||
|
||||
# TODO: use ModelFactory
|
||||
self.custom_mods = Gtk.TreeView()
|
||||
# TODO: suppress symlink column
|
||||
self.custom_tree = OfflineModTreeView(controller)
|
||||
|
||||
# TODO: shared ConnectBox class
|
||||
# TODO: share ConnectBox class with preconnect dialog
|
||||
self.button_box = HBox()
|
||||
self.back = Gtk.Button()
|
||||
self.ok = Gtk.Button()
|
||||
self.back.connect("clicked", self._on_back_clicked)
|
||||
self.ok.connect("clicked", self._on_ok_clicked)
|
||||
self.connect("key-press-event", self._on_keypress)
|
||||
|
||||
self.button_box.extend([self.back, self.ok])
|
||||
|
||||
def _on_keypress(self, widget: Self, event: Gdk.EventKey) -> None:
|
||||
# TODO: consider dropping this
|
||||
if event.keyval == Gdk.KEY_Escape:
|
||||
self.back.emit("clicked")
|
||||
|
||||
def _on_selection_changed(self, sel: Gtk.TreeSelection) -> None:
|
||||
model, rows = sel.get_selected_rows()
|
||||
# TODO: update sub-statusbar with selected mods
|
||||
print(len(rows))
|
||||
|
||||
def populate(self, mods: Gtk.TreeModel | None) -> None:
|
||||
self.local_mods.set_model(mods)
|
||||
# TODO: push existing model
|
||||
# TODO: set sensitivity of radio buttons
|
||||
pass
|
||||
|
||||
def _on_custom_button_clicked(self, button: Gtk.Button) -> None:
|
||||
# TODO: recycle this for both buttons
|
||||
folder = self.controller.set_custom_folder()
|
||||
if folder is not None:
|
||||
# TODO: parse mods from here
|
||||
# TODO: offline mod manager would be cleaner
|
||||
print(folder)
|
||||
def populate(self, store: "FastInsertListStore") -> None:
|
||||
self.local_frame.set_model(store)
|
||||
# TODO: toggle if empty model, show warning label
|
||||
# TODO: suppress trees if there are no mods
|
||||
self.custom_frame.collapse_tree()
|
||||
|
||||
def _on_back_clicked(self, button: Gtk.Button) -> None:
|
||||
from dzgui.const.enum import NotebookPage
|
||||
|
||||
# FIXME: returns to server page and not parent mod page
|
||||
# consider using a toggle button instead, and suppressing statusbar
|
||||
self.controller.open_page(NotebookPage.SERVERS)
|
||||
|
||||
def _on_ok_clicked(self, button: Gtk.Button) -> None:
|
||||
"""
|
||||
- collect symlinks to selected mods
|
||||
cf. rebuild_symlinks()
|
||||
- create symlinks for custom mods
|
||||
- collect appid
|
||||
- collect mission folder
|
||||
"""
|
||||
pass
|
||||
"""
|
||||
create symlinks if there are custom mods
|
||||
cf. rebuild_symlinks()
|
||||
"""
|
||||
|
||||
@ -187,12 +187,14 @@ class Options(Gtk.Box):
|
||||
if prefs.is_debug:
|
||||
grid.attach(developers, 1, 0, self.DEFAULT_WIDTH, self.DEFAULT_HEIGHT)
|
||||
|
||||
for frame in [
|
||||
HeadingFrame(api_box, strings.options.api_keys),
|
||||
HeadingFrame(prefs_grid, strings.options.prefs),
|
||||
HeadingFrame(mods_grid, strings.options.mods),
|
||||
HeadingFrame(version_grid, strings.options.version),
|
||||
for pair in [
|
||||
(api_box, strings.options.api_keys),
|
||||
(prefs_grid, strings.options.prefs),
|
||||
(mods_grid, strings.options.mods),
|
||||
(version_grid, strings.options.version),
|
||||
]:
|
||||
|
||||
frame = HeadingFrame.new_with_widget_and_label(*pair)
|
||||
grid.attach(frame, col, row, self.DEFAULT_WIDTH, self.DEFAULT_HEIGHT)
|
||||
row += 1
|
||||
|
||||
|
||||
@ -166,7 +166,9 @@ class PreConnectionAssistant(Gtk.Box):
|
||||
self.mods_placeholder = Placeholder("This server has no mods.")
|
||||
self.tree_box.add(self.mods_placeholder)
|
||||
|
||||
self.tree_frame = HeadingFrame(self.tree_box, preconnect.mods)
|
||||
self.tree_frame = HeadingFrame.new_with_widget_and_label(
|
||||
self.tree_box, preconnect.mods
|
||||
)
|
||||
|
||||
# TODO: abstract into components
|
||||
self.warning_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
@ -175,7 +177,9 @@ class PreConnectionAssistant(Gtk.Box):
|
||||
self.warning_placeholder = Placeholder("No warnings.")
|
||||
self.warning_box.add(self.warning_tree)
|
||||
self.warning_box.add(self.warning_placeholder)
|
||||
self.warning_frame = HeadingFrame(self.warning_box, preconnect.warnings)
|
||||
self.warning_frame = HeadingFrame.new_with_widget_and_label(
|
||||
self.warning_box, preconnect.warnings
|
||||
)
|
||||
|
||||
self.error_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
self.error_tree = MaskedTree(ERROR)
|
||||
@ -183,7 +187,9 @@ class PreConnectionAssistant(Gtk.Box):
|
||||
self.error_placeholder = Placeholder("No errors.")
|
||||
self.error_box.add(self.error_tree)
|
||||
self.error_box.add(self.error_placeholder)
|
||||
self.error_frame = HeadingFrame(self.error_box, preconnect.errors)
|
||||
self.error_frame = HeadingFrame.new_with_widget_and_label(
|
||||
self.error_box, preconnect.errors
|
||||
)
|
||||
|
||||
self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
|
||||
self.box.add(self.title)
|
||||
|
||||
@ -4,7 +4,7 @@ from typing import Any, Optional, TYPE_CHECKING
|
||||
from warnings import deprecated
|
||||
|
||||
from dzgui.const.constants import APP_NAME, SEPARATOR
|
||||
from dzgui.util.keys import is_navkey
|
||||
from dzgui.util.keys import is_ctrl_mask, is_navkey
|
||||
from dzgui.views.mixins.cursor_mixin import CursorMixin
|
||||
|
||||
import gi
|
||||
@ -85,10 +85,17 @@ class TreeView(CursorMixin, Gtk.TreeView): # type: ignore
|
||||
model, rows = sel.get_selected_rows()
|
||||
return [model[row] for row in rows]
|
||||
|
||||
def _on_keypress(self, treeview: Gtk.TreeView, event: Gdk.EventKey) -> None:
|
||||
def _on_keypress(self, treeview: Gtk.TreeView, event: Gdk.EventKey) -> bool:
|
||||
from dzgui.views.trees.tree_servers import ServerTreeView
|
||||
|
||||
# NOTE: suppress popup search and delegate binding to server view (cf. request_keyword_focus)
|
||||
if not isinstance(treeview, ServerTreeView):
|
||||
if is_ctrl_mask(event) and event.keyval == Gdk.KEY_f:
|
||||
return True
|
||||
|
||||
if is_navkey(event.keyval):
|
||||
if self.get_model() is None:
|
||||
return
|
||||
return True
|
||||
if self.sel_blocked is False:
|
||||
self.sel_blocked = True
|
||||
self.controller.suppress_signal(
|
||||
@ -98,7 +105,7 @@ class TreeView(CursorMixin, Gtk.TreeView): # type: ignore
|
||||
True,
|
||||
)
|
||||
self._vim_nav(event)
|
||||
return
|
||||
return False
|
||||
|
||||
def _on_key_release(self, treeview: Gtk.TreeView, event: Gdk.EventKey) -> None:
|
||||
"""
|
||||
|
||||
@ -3,8 +3,8 @@ from typing import Any, TYPE_CHECKING
|
||||
|
||||
from dzgui.const.constants import APP_NAME, HEX_RED
|
||||
from dzgui.const.enum import ContextMenuGroup
|
||||
from dzgui.managers.mods import ModManager
|
||||
from dzgui.util import strings, localize
|
||||
from dzgui.views.mixins.cursor_mixin import CursorMixin
|
||||
from dzgui.views.mixins.context_mixin import ContextMixin
|
||||
from dzgui.views.mixins.mods_mixin import ModsMixin
|
||||
from dzgui.views.trees.tree_base import TreeView
|
||||
@ -25,7 +25,10 @@ logger = logging.getLogger(APP_NAME)
|
||||
class ModTreeView(ModsMixin, ContextMixin, TreeView): # type: ignore
|
||||
def __init__(self, controller: "Controller") -> None:
|
||||
super().__init__(controller, menu=ContextMenuGroup.MOD)
|
||||
|
||||
self.controller = controller
|
||||
self.mod_man = ModManager(self, controller)
|
||||
|
||||
emitter = self.controller.get_emitter()
|
||||
emitter.connect("mods_updated", self._on_mods_updated)
|
||||
emitter.connect("mods_highlighted", self._on_mods_highlighted)
|
||||
@ -57,6 +60,12 @@ class ModTreeView(ModsMixin, ContextMixin, TreeView): # type: ignore
|
||||
self.connect("button-press-event", self.present_menu)
|
||||
self.connect("key-press-event", self.present_menu)
|
||||
|
||||
def get_mod_man(self) -> ModManager:
|
||||
return self.mod_man
|
||||
|
||||
def load_mods(self) -> None:
|
||||
self.mod_man.load_mods()
|
||||
|
||||
def _on_mods_highlighted(self, emitter: "Emitter") -> None:
|
||||
self.get_selection().unselect_all()
|
||||
|
||||
@ -106,3 +115,10 @@ class ModTreeView(ModsMixin, ContextMixin, TreeView): # type: ignore
|
||||
val = model[it][3]
|
||||
formatted = localize.number(val)
|
||||
cell.set_property("text", formatted)
|
||||
|
||||
def set_menu(self, context: ContextMenuGroup) -> None:
|
||||
self.menu = context
|
||||
|
||||
class OfflineModTreeView(ModTreeView):
|
||||
def __init__(self, controller: "Controller") -> None:
|
||||
super().__init__(controller)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user