mirror of
https://github.com/aclist/dztui.git
synced 2026-11-01 15:42:51 +01:00
feat: add navigable context menu to mixins
This commit is contained in:
1 parent
ac7700ec23
commit
56b32a0048
4 files changed
+83
-104
No files matched your search
+16
-1
@@ -434,6 +434,20 @@ class Controller(GObject.GObject):
|
|||||||
text = "\n".join(final)
|
text = "\n".join(final)
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
def copy_ip(self, path: Gtk.TreePath) -> None:
|
||||||
|
treeview = self.get_active_treeview()
|
||||||
|
model = treeview.get_model()
|
||||||
|
if not model:
|
||||||
|
return None
|
||||||
|
addr = model[path][7]
|
||||||
|
if addr is None:
|
||||||
|
return
|
||||||
|
qport = model[path][8]
|
||||||
|
ip = addr.split(":")[0]
|
||||||
|
gameport = int(addr.split(":")[1])
|
||||||
|
record = Servers.Record(ip, gameport, qport)
|
||||||
|
self.copy_clipboard(f"{record.ip}:{record.qport}")
|
||||||
|
|
||||||
def copy_clipboard(self, text: str) -> None:
|
def copy_clipboard(self, text: str) -> None:
|
||||||
self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
||||||
self.clipboard.set_text(text, -1)
|
self.clipboard.set_text(text, -1)
|
||||||
@@ -503,8 +517,9 @@ class Controller(GObject.GObject):
|
|||||||
case ContextMenu.ADD_NOTE:
|
case ContextMenu.ADD_NOTE:
|
||||||
pass
|
pass
|
||||||
case ContextMenu.COPY_CLIPBOARD:
|
case ContextMenu.COPY_CLIPBOARD:
|
||||||
pass
|
self.copy_ip(path)
|
||||||
case ContextMenu.COPY_NAME:
|
case ContextMenu.COPY_NAME:
|
||||||
|
self.copy_name(path)
|
||||||
pass
|
pass
|
||||||
case ContextMenu.REFRESH_PLAYERS:
|
case ContextMenu.REFRESH_PLAYERS:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
from dzgui.views.trees.tree_base import TreeView
|
|
||||||
from dzgui.const.enum import ContextMenu
|
from dzgui.const.enum import ContextMenu
|
||||||
|
from dzgui.util.keys import is_navkey
|
||||||
|
from dzgui.views.trees.tree_base import TreeView
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ class ContextMixin(TreeView):
|
|||||||
if self.is_selection_empty():
|
if self.is_selection_empty():
|
||||||
return
|
return
|
||||||
|
|
||||||
if event.type is Gdk.EventType.BUTTON_PRESS:
|
if event.type is Gdk.EventType.BUTTON_PRESS and event.button == 3:
|
||||||
try:
|
try:
|
||||||
pathinfo = self.get_path_at_pos(int(event.x), int(event.y))
|
pathinfo = self.get_path_at_pos(int(event.x), int(event.y))
|
||||||
if pathinfo is None:
|
if pathinfo is None:
|
||||||
@@ -41,6 +42,8 @@ class ContextMixin(TreeView):
|
|||||||
|
|
||||||
group = self.menu
|
group = self.menu
|
||||||
self.context_menu = Gtk.Menu()
|
self.context_menu = Gtk.Menu()
|
||||||
|
self.context_menu.connect("key-press-event", self._on_key)
|
||||||
|
|
||||||
for row in group.value:
|
for row in group.value:
|
||||||
item = Gtk.MenuItem(label=row.dict["label"])
|
item = Gtk.MenuItem(label=row.dict["label"])
|
||||||
item.connect("activate", self._on_menu_click, row)
|
item.connect("activate", self._on_menu_click, row)
|
||||||
@@ -63,11 +66,11 @@ class ContextMixin(TreeView):
|
|||||||
Local mods page allows multi selection, so ensure that only focused row
|
Local mods page allows multi selection, so ensure that only focused row
|
||||||
is selected. Used by ContextMenu.OPEN_WORKSHOP and ContextMenu.DELETE_MOD.
|
is selected. Used by ContextMenu.OPEN_WORKSHOP and ContextMenu.DELETE_MOD.
|
||||||
|
|
||||||
Mod tree right panel supports multi-delete, but context menu enforces
|
ModTreeView supports multi-delete, but context menu enforces
|
||||||
single deletion on the focused row.
|
single deletion on the focused row.
|
||||||
|
|
||||||
Debug log page allows multi selection and copy of rows, so special handling
|
Debug log page allows multi selection and copy of rows, so special handling
|
||||||
is used.
|
is used per below.
|
||||||
"""
|
"""
|
||||||
if enum == ContextMenu.COPY_LOG_CLIPBOARD:
|
if enum == ContextMenu.COPY_LOG_CLIPBOARD:
|
||||||
model, records = self.get_selection().get_selected_rows()
|
model, records = self.get_selection().get_selected_rows()
|
||||||
@@ -76,3 +79,32 @@ class ContextMixin(TreeView):
|
|||||||
else:
|
else:
|
||||||
path = self.get_focused_row_path()
|
path = self.get_focused_row_path()
|
||||||
self.controller.menu_action(enum, path)
|
self.controller.menu_action(enum, path)
|
||||||
|
|
||||||
|
def _on_key(self, menu: Gtk.Menu, event: Gdk.EventKey) -> bool | None:
|
||||||
|
if not is_navkey(event.keyval):
|
||||||
|
return False
|
||||||
|
menu = self.context_menu
|
||||||
|
sel = menu.get_selected_item()
|
||||||
|
children = menu.get_children()
|
||||||
|
for i, child in enumerate(children):
|
||||||
|
if sel is child:
|
||||||
|
ind = i
|
||||||
|
break
|
||||||
|
|
||||||
|
match event.keyval:
|
||||||
|
case Gdk.KEY_j:
|
||||||
|
if ind == len(children) - 1:
|
||||||
|
return True
|
||||||
|
menu.select_item(children[ind + 1])
|
||||||
|
case Gdk.KEY_k:
|
||||||
|
if ind - 1 < 0:
|
||||||
|
return True
|
||||||
|
menu.select_item(children[ind - 1])
|
||||||
|
case Gdk.KEY_g:
|
||||||
|
menu.select_item(children[0])
|
||||||
|
case Gdk.KEY_G:
|
||||||
|
ind = len(children) - 1
|
||||||
|
menu.select_item(children[ind])
|
||||||
|
case _:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
@@ -50,28 +50,28 @@ class ModTreeView(ModsMixin, ContextMixin, TreeView):
|
|||||||
|
|
||||||
self.connect("generic_row_activated", self._on_mod_row_activated)
|
self.connect("generic_row_activated", self._on_mod_row_activated)
|
||||||
self.connect("generic_treesel_changed", self._parent_selection_changed)
|
self.connect("generic_treesel_changed", self._parent_selection_changed)
|
||||||
self.connect("button-press-event", self._on_mods_button_press)
|
self.connect("button-press-event", self.present_menu)
|
||||||
self.connect("key-press-event", self._on_mods_keypress)
|
self.connect("key-press-event", self.present_menu)
|
||||||
self.connect("map", self._on_map)
|
self.connect("map", self._on_map)
|
||||||
|
|
||||||
def _on_map(self, tree: Self) -> None:
|
def _on_map(self, tree: Self) -> None:
|
||||||
# TODO: placeholder logic
|
# FIXME: placeholder logic
|
||||||
# identify root cause of row unfocus on init
|
# identify root cause of row unfocus on init
|
||||||
self.set_cursor(0)
|
self.set_cursor(0)
|
||||||
|
|
||||||
def _on_mods_keypress(self, widget: Gtk.Widget, event: Gdk.EventKey) -> None:
|
# def _on_mods_keypress(self, widget: Gtk.Widget, event: Gdk.EventKey) -> None:
|
||||||
# TODO: multiselect
|
# # TODO: multiselect
|
||||||
# if event.keyval is Gdk.KEY_space:
|
# # if event.keyval is Gdk.KEY_space:
|
||||||
# it = self.get_focused_row_iter()
|
# # it = self.get_focused_row_iter()
|
||||||
# self.get_selection().select_iter(it)
|
# # self.get_selection().select_iter(it)
|
||||||
# path = self.get_focused_row_path()
|
# # path = self.get_focused_row_path()
|
||||||
# self.set_cursor(path)
|
# # self.set_cursor(path)
|
||||||
# return False
|
# # return False
|
||||||
self.present_menu(widget, event)
|
# self.present_menu(widget, event)
|
||||||
|
|
||||||
def _on_mods_button_press(self, widget: Gtk.Widget, event: Gdk.EventButton) -> None:
|
# def _on_mods_button_press(self, widget: Gtk.Widget, event: Gdk.EventButton) -> None:
|
||||||
if event.button == 3:
|
# if event.button == 3:
|
||||||
self.present_menu(widget, event)
|
# self.present_menu(widget, event)
|
||||||
|
|
||||||
def _parent_selection_changed(self, base_class: TreeView, sel: Gtk.TreeSelection):
|
def _parent_selection_changed(self, base_class: TreeView, sel: Gtk.TreeSelection):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import multiprocessing
|
|||||||
from typing import Any, Self
|
from typing import Any, Self
|
||||||
from warnings import deprecated
|
from warnings import deprecated
|
||||||
|
|
||||||
from dzgui.const.enum import ContextMenu, ContextMenuGroup, ServerTab
|
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 Record
|
||||||
from dzgui.model.filtered_model import FilteredModelManager
|
from dzgui.model.filtered_model import FilteredModelManager
|
||||||
from dzgui.util.dist import CalcDist
|
from dzgui.util.dist import CalcDist
|
||||||
from dzgui.util.keys import is_navkey
|
|
||||||
from dzgui.util import strings
|
from dzgui.util import strings
|
||||||
from typing import Callable, Literal, TYPE_CHECKING
|
from typing import Callable, Literal, TYPE_CHECKING
|
||||||
|
|
||||||
@@ -26,17 +26,11 @@ if TYPE_CHECKING:
|
|||||||
from dzgui.controllers.mc import Controller
|
from dzgui.controllers.mc import Controller
|
||||||
|
|
||||||
|
|
||||||
class EnumeratedMenuItem(Gtk.MenuItem):
|
class ServerTreeView(ContextMixin, TreeView):
|
||||||
def __init__(self, enum: ContextMenu):
|
|
||||||
super().__init__(label=enum.dict["label"])
|
|
||||||
self.enum = enum
|
|
||||||
|
|
||||||
|
|
||||||
class ServerTreeView(TreeView):
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, controller: "Controller", enum: ServerTab, menu: ContextMenuGroup
|
self, controller: "Controller", enum: ServerTab, menu: ContextMenuGroup
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(controller)
|
super().__init__(controller, menu=ContextMenuGroup.SERVER_BROWSER)
|
||||||
|
|
||||||
QUEUE_CHECK_DELAY = 200
|
QUEUE_CHECK_DELAY = 200
|
||||||
|
|
||||||
@@ -51,10 +45,6 @@ class ServerTreeView(TreeView):
|
|||||||
model = self.filter_man.get_model()
|
model = self.filter_man.get_model()
|
||||||
self.set_model(model)
|
self.set_model(model)
|
||||||
|
|
||||||
self.menu = Gtk.Menu()
|
|
||||||
self.menu.connect("key-press-event", self._on_key)
|
|
||||||
|
|
||||||
self.set_context_menu(menu)
|
|
||||||
self.set_fixed_height_mode(True)
|
self.set_fixed_height_mode(True)
|
||||||
self.set_headers_visible(True)
|
self.set_headers_visible(True)
|
||||||
|
|
||||||
@@ -104,13 +94,15 @@ class ServerTreeView(TreeView):
|
|||||||
self.append_column(column)
|
self.append_column(column)
|
||||||
|
|
||||||
self.connect("distcalc_started", self._on_distcalc_started)
|
self.connect("distcalc_started", self._on_distcalc_started)
|
||||||
self.connect("button-release-event", self._on_server_button_release)
|
|
||||||
self.connect("key-press-event", self._on_server_keypress)
|
self.connect("key-press-event", self._on_server_keypress)
|
||||||
self.connect("generic_row_activated", self._parent_row_activated)
|
self.connect("generic_row_activated", self._parent_row_activated)
|
||||||
self.connect("generic_treesel_changed", self._parent_selection_changed)
|
self.connect("generic_treesel_changed", self._parent_selection_changed)
|
||||||
self.connect("map", self._on_map)
|
self.connect("map", self._on_map)
|
||||||
self.connect("unmap", self._on_unmap)
|
self.connect("unmap", self._on_unmap)
|
||||||
|
|
||||||
|
self.connect("key-press-event", self.present_menu)
|
||||||
|
self.connect("button-press-event", self.present_menu)
|
||||||
|
|
||||||
GLib.timeout_add(QUEUE_CHECK_DELAY, self._check_result_queue)
|
GLib.timeout_add(QUEUE_CHECK_DELAY, self._check_result_queue)
|
||||||
|
|
||||||
@GObject.Signal(flags=GObject.SignalFlags.RUN_FIRST, arg_types=())
|
@GObject.Signal(flags=GObject.SignalFlags.RUN_FIRST, arg_types=())
|
||||||
@@ -154,35 +146,6 @@ class ServerTreeView(TreeView):
|
|||||||
if self.get_enum() is ServerTab.LAN:
|
if self.get_enum() is ServerTab.LAN:
|
||||||
self.controller.mediator.grid.conpan.lan.set_visible(False)
|
self.controller.mediator.grid.conpan.lan.set_visible(False)
|
||||||
|
|
||||||
def _on_key(self, menu: Gtk.Menu, event: Gdk.EventKey) -> bool | None:
|
|
||||||
# TODO: abstract as navigable context menu
|
|
||||||
if not is_navkey(event.keyval):
|
|
||||||
return False
|
|
||||||
sel = menu.get_selected_item()
|
|
||||||
children = menu.get_children()
|
|
||||||
for i, child in enumerate(children):
|
|
||||||
if sel is child:
|
|
||||||
ind = i
|
|
||||||
break
|
|
||||||
|
|
||||||
match event.keyval:
|
|
||||||
case Gdk.KEY_j:
|
|
||||||
if ind == len(children) - 1:
|
|
||||||
return True
|
|
||||||
menu.select_item(children[ind + 1])
|
|
||||||
case Gdk.KEY_k:
|
|
||||||
if ind - 1 < 0:
|
|
||||||
return True
|
|
||||||
menu.select_item(children[ind - 1])
|
|
||||||
case Gdk.KEY_g:
|
|
||||||
menu.select_item(children[0])
|
|
||||||
case Gdk.KEY_G:
|
|
||||||
ind = len(children) - 1
|
|
||||||
menu.select_item(children[ind])
|
|
||||||
case _:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def set_query_func(self, func: Callable) -> None:
|
def set_query_func(self, func: Callable) -> None:
|
||||||
self.query_func = func
|
self.query_func = func
|
||||||
|
|
||||||
@@ -241,8 +204,6 @@ class ServerTreeView(TreeView):
|
|||||||
) -> bool | None:
|
) -> bool | None:
|
||||||
if event.state is Gdk.ModifierType.CONTROL_MASK:
|
if event.state is Gdk.ModifierType.CONTROL_MASK:
|
||||||
match event.keyval:
|
match event.keyval:
|
||||||
case Gdk.KEY_l:
|
|
||||||
self._on_server_button_release(self, event)
|
|
||||||
case Gdk.KEY_r:
|
case Gdk.KEY_r:
|
||||||
self.refresh_player_count()
|
self.refresh_player_count()
|
||||||
case Gdk.KEY_f:
|
case Gdk.KEY_f:
|
||||||
@@ -255,7 +216,8 @@ class ServerTreeView(TreeView):
|
|||||||
if self.enum is ServerTab.LAN:
|
if self.enum is ServerTab.LAN:
|
||||||
self.emitter.emit("request_lan_entry_focus", self.enum)
|
self.emitter.emit("request_lan_entry_focus", self.enum)
|
||||||
case Gdk.KEY_c:
|
case Gdk.KEY_c:
|
||||||
print("UNIMPLEMENTED: copy to clipboard")
|
record = self.get_record()
|
||||||
|
self.controller.copy_ip(record)
|
||||||
else:
|
else:
|
||||||
match event.keyval:
|
match event.keyval:
|
||||||
case Gdk.KEY_l | Gdk.KEY_Right:
|
case Gdk.KEY_l | Gdk.KEY_Right:
|
||||||
@@ -263,37 +225,7 @@ class ServerTreeView(TreeView):
|
|||||||
case _:
|
case _:
|
||||||
self.emitter.emit("check_button_pressed", event.keyval)
|
self.emitter.emit("check_button_pressed", event.keyval)
|
||||||
|
|
||||||
def set_context_menu(self, items: ContextMenuGroup) -> None:
|
|
||||||
# TODO: if debug is on, add raw command copy to context menu
|
|
||||||
for item in items.value:
|
|
||||||
menu_item = EnumeratedMenuItem(item)
|
|
||||||
menu_item.connect("activate", self._on_menu_click)
|
|
||||||
self.menu.append(menu_item)
|
|
||||||
self.menu.show_all()
|
|
||||||
|
|
||||||
def _on_menu_click(self, item) -> None:
|
|
||||||
print(f"UNIMPLEMENTED: {item.enum}")
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _on_server_button_release(
|
|
||||||
self, widget: Gtk.Widget, event: Gdk.EventButton
|
|
||||||
) -> None:
|
|
||||||
# TODO: use ContextMixin
|
|
||||||
if event.type is Gdk.EventType.BUTTON_RELEASE and event.button != 3:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
pathinfo = self.get_path_at_pos(int(event.x), int(event.y))
|
|
||||||
if pathinfo is None:
|
|
||||||
return
|
|
||||||
(path, col, cellx, celly) = pathinfo
|
|
||||||
if path is None:
|
|
||||||
return
|
|
||||||
self.set_cursor(path, col, False)
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# mod_context_items = [ContextMenu.OPEN_WORKSHOP, ContextMenu.DELETE_MOD]
|
# mod_context_items = [ContextMenu.OPEN_WORKSHOP, ContextMenu.DELETE_MOD]
|
||||||
|
|
||||||
# TODO: dynamic menu entries
|
# TODO: dynamic menu entries
|
||||||
# for row in items:
|
# for row in items:
|
||||||
# if row == ContextMenu.ADD_SERVER:
|
# if row == ContextMenu.ADD_SERVER:
|
||||||
@@ -310,13 +242,13 @@ class ServerTreeView(TreeView):
|
|||||||
# if self.get_record_string() in notes_cache:
|
# if self.get_record_string() in notes_cache:
|
||||||
# item.set_label(strings.edit_note)
|
# item.set_label(strings.edit_note)
|
||||||
|
|
||||||
if event.type is Gdk.EventType.KEY_PRESS and event.keyval is Gdk.KEY_l:
|
# if event.type is Gdk.EventType.KEY_PRESS and event.keyval is Gdk.KEY_l:
|
||||||
if self.is_selection_empty():
|
# if self.is_selection_empty():
|
||||||
return
|
# return
|
||||||
self.menu.popup_at_widget(widget, Gdk.Gravity.CENTER, Gdk.Gravity.WEST)
|
# self.menu.popup_at_widget(widget, Gdk.Gravity.CENTER, Gdk.Gravity.WEST)
|
||||||
else:
|
# else:
|
||||||
self.menu.popup_at_pointer(event)
|
# self.menu.popup_at_pointer(event)
|
||||||
self.menu.select_first(False)
|
# self.menu.select_first(False)
|
||||||
|
|
||||||
def _parent_row_activated(
|
def _parent_row_activated(
|
||||||
self, tree: TreeView, path: Gtk.TreePath, column: Gtk.TreeViewColumn
|
self, tree: TreeView, path: Gtk.TreePath, column: Gtk.TreeViewColumn
|
||||||
|
|||||||
Reference in new issue
Block a user