mirror of
https://github.com/aclist/dztui.git
synced 2026-08-25 17:32:36 +02:00
344 lines
12 KiB
Python
344 lines
12 KiB
Python
from typing import Self, TYPE_CHECKING
|
|
|
|
from dzgui.const.constants import CARET_DOWN, CARET_UP, UDP_PORT
|
|
from dzgui.model.servers import ServerModelManager
|
|
from dzgui.strings import connect_panel
|
|
from dzgui.util.keys import is_ctrl_mask
|
|
from dzgui.views.components.buttons import (
|
|
AddButton,
|
|
CloseButton,
|
|
CopyIpButton,
|
|
Icon,
|
|
IconButton,
|
|
SteamConnectButton,
|
|
)
|
|
from dzgui.views.components.entry import IpEntry, PortEntry
|
|
from dzgui.views.components.labels import BoldLabel
|
|
|
|
import gi
|
|
|
|
gi.require_version("Gtk", "3.0")
|
|
from gi.repository import Gtk, Gdk # noqa E402
|
|
|
|
if TYPE_CHECKING:
|
|
from dzgui.controllers.mc import Controller
|
|
from dzgui.controllers.emitter import Emitter
|
|
|
|
COLS = 1
|
|
ROWS = 1
|
|
|
|
|
|
class DockButton(IconButton):
|
|
def __init__(self, child: Gtk.Widget) -> None:
|
|
super().__init__(CARET_DOWN)
|
|
|
|
self.expanded = True
|
|
self.child = child
|
|
self.connect("clicked", self._on_click)
|
|
|
|
self.collapse_text = "Collapse the connection panel"
|
|
self.expand_text = "Expand the connection panel"
|
|
|
|
self.set_margin_top(10)
|
|
self.set_tooltip_text(self.collapse_text)
|
|
|
|
def collapse(self) -> None:
|
|
self.set_image(Icon(CARET_UP))
|
|
self.child.hide()
|
|
self.set_tooltip_text(self.expand_text)
|
|
|
|
def expand(self) -> None:
|
|
self.set_image(Icon(CARET_DOWN))
|
|
self.child.show()
|
|
self.set_tooltip_text(self.collapse_text)
|
|
|
|
def _on_click(self, button: Self) -> None:
|
|
if self.expanded:
|
|
self.collapse()
|
|
else:
|
|
self.expand()
|
|
self.expanded = not self.expanded
|
|
|
|
|
|
class LanPanel(Gtk.Frame):
|
|
def __init__(self, controller: "Controller") -> None:
|
|
super().__init__(margin_top=10, margin_bottom=5)
|
|
|
|
self.controller = controller
|
|
self.emitter = controller.get_emitter()
|
|
|
|
label = BoldLabel(connect_panel.lan_heading)
|
|
self.set_label_widget(label)
|
|
|
|
self.default_radio = Gtk.RadioButton.new_with_label(
|
|
None, connect_panel.lan_default_button
|
|
)
|
|
self.custom_radio = Gtk.RadioButton.new_with_label_from_widget(
|
|
self.default_radio, connect_panel.lan_custom_button
|
|
)
|
|
|
|
self.entry = PortEntry(controller)
|
|
self.scan = Gtk.Button(label=connect_panel.lan_scan_button)
|
|
self.scan.connect("clicked", self._on_scan_clicked)
|
|
|
|
# TODO: strings
|
|
self.early_abort = Gtk.CheckButton(label=connect_panel.lan_checkbox)
|
|
self.early_abort.set_active(True)
|
|
self.early_abort.set_tooltip_text(connect_panel.lan_abort_tooltip)
|
|
|
|
self.entry.connect("activate", self._on_entry_activated)
|
|
self.entry.connect("string_validated", self._on_port_validated)
|
|
self.entry.connect("key-release-event", self._on_lan_keypress)
|
|
self.emitter.connect("request_custom_port_focus", self._on_custom_port_binding)
|
|
self.emitter.connect(
|
|
"request_default_port_focus", lambda _: self.default_radio.set_active(True)
|
|
)
|
|
self.default_radio.connect("toggled", self._on_radio_toggled)
|
|
|
|
self.grid = Gtk.Grid(margin=10, vexpand=False, column_spacing=15, row_spacing=5)
|
|
self.grid.attach(self.default_radio, 0, 0, COLS, ROWS)
|
|
self.grid.attach_next_to(
|
|
self.custom_radio, self.default_radio, Gtk.PositionType.RIGHT, COLS, ROWS
|
|
)
|
|
self.grid.attach_next_to(
|
|
self.entry, self.custom_radio, Gtk.PositionType.RIGHT, COLS, ROWS
|
|
)
|
|
self.grid.attach_next_to(
|
|
self.scan, self.entry, Gtk.PositionType.RIGHT, COLS, ROWS
|
|
)
|
|
self.grid.attach_next_to(
|
|
self.early_abort, self.scan, Gtk.PositionType.RIGHT, COLS, ROWS
|
|
)
|
|
|
|
self.add(self.grid)
|
|
|
|
self.entry.set_sensitive(False)
|
|
|
|
def _on_lan_keypress(self, widget: Gtk.Entry, event: Gdk.EventKey) -> bool:
|
|
if is_ctrl_mask(event):
|
|
if event.keyval == Gdk.KEY_d:
|
|
self.default_radio.set_active(True)
|
|
self.controller.grab_active_treeview()
|
|
return True
|
|
return False
|
|
|
|
def _on_custom_port_binding(self, emitter: "Emitter") -> None:
|
|
self.custom_radio.set_active(True)
|
|
self.entry.grab_focus()
|
|
|
|
def _on_entry_activated(self, entry: Gtk.Entry) -> None:
|
|
if self.entry.get_text() == "":
|
|
return
|
|
self.scan_ports()
|
|
|
|
def _on_scan_clicked(self, button: Gtk.Button) -> None:
|
|
self.scan_ports()
|
|
|
|
def scan_ports(self) -> None:
|
|
if self.default_radio.get_active():
|
|
port = UDP_PORT
|
|
else:
|
|
port = int(self.entry.get_text())
|
|
abort = self.early_abort.get_active()
|
|
smm = ServerModelManager(self.controller, self.controller.get_active_treeview())
|
|
smm.dump_lan(port, abort)
|
|
|
|
def _on_radio_toggled(self, button: Gtk.RadioButton) -> None:
|
|
state = button.get_active()
|
|
self.entry.set_sensitive(not state)
|
|
if state:
|
|
self.scan.set_sensitive(True)
|
|
else:
|
|
self.entry.grab_focus()
|
|
if len(self.entry.get_text()) < 1:
|
|
self.scan.set_sensitive(False)
|
|
|
|
def _on_port_validated(self, entry: Gtk.Entry, state: bool) -> None:
|
|
self.scan.set_sensitive(state)
|
|
|
|
|
|
class FavPanel(Gtk.Frame):
|
|
def __init__(self, controller: "Controller") -> None:
|
|
super().__init__(margin_top=10, margin_bottom=5)
|
|
|
|
label = BoldLabel(connect_panel.fav_heading)
|
|
self.set_label_widget(label)
|
|
|
|
self.controller = controller
|
|
emitter = self.controller.get_emitter()
|
|
emitter.connect("fav_server_changed", self._on_fav_server_changed)
|
|
|
|
self.server_name = ""
|
|
self.server_ip = ""
|
|
|
|
favorite = self.controller.get_config_man().get_favorite()
|
|
if favorite is None:
|
|
server_name = connect_panel.favs_empty
|
|
else:
|
|
self.server_name, self.server_ip = favorite
|
|
server_name = f"{self.server_name} ({self.server_ip})"
|
|
|
|
self.fav_label = Gtk.Label(
|
|
label=server_name,
|
|
track_visited_links=False,
|
|
halign=Gtk.Align.START,
|
|
hexpand=True,
|
|
)
|
|
|
|
self.fav_button = SteamConnectButton()
|
|
self.fav_button.connect("clicked", self._on_connect_clicked)
|
|
self.copy_button = CopyIpButton(self.controller, self.get_fav_ip)
|
|
|
|
# TODO: strings
|
|
self.unset_button = CloseButton("Unset")
|
|
self.unset_button.connect("clicked", self._on_unset_clicked)
|
|
|
|
if favorite is None:
|
|
self.toggle_buttons(False)
|
|
|
|
# NOTE: disable vscrollbar to prevent layout jumping behavior
|
|
self.scrollable_label = Gtk.ScrolledWindow(
|
|
vscrollbar_policy=Gtk.PolicyType.NEVER, overlay_scrolling=False
|
|
)
|
|
self.scrollable_label.add(self.fav_label)
|
|
|
|
grid = Gtk.Grid(margin=10, vexpand=False, column_spacing=15, row_spacing=5)
|
|
grid.attach(self.scrollable_label, 0, 0, 3, ROWS)
|
|
grid.attach_next_to(
|
|
self.unset_button, self.scrollable_label, Gtk.PositionType.RIGHT, COLS, ROWS
|
|
)
|
|
grid.attach_next_to(
|
|
self.copy_button, self.unset_button, Gtk.PositionType.RIGHT, COLS, ROWS
|
|
)
|
|
grid.attach_next_to(
|
|
self.fav_button, self.copy_button, Gtk.PositionType.RIGHT, COLS, ROWS
|
|
)
|
|
|
|
self.add(grid)
|
|
|
|
def _on_unset_clicked(self, button: Gtk.Button) -> None:
|
|
try:
|
|
self.controller.unset_fav()
|
|
except Exception:
|
|
return
|
|
self.fav_label.set_text(connect_panel.favs_empty)
|
|
self.toggle_buttons(False)
|
|
|
|
def toggle_buttons(self, state: bool) -> None:
|
|
for button in self.fav_button, self.copy_button, self.unset_button:
|
|
button.set_sensitive(state)
|
|
|
|
def _on_connect_clicked(self, button: Gtk.Button) -> None:
|
|
self.controller.connect_by_str(self.server_ip)
|
|
|
|
def get_fav_ip(self) -> str:
|
|
return self.server_ip
|
|
|
|
def _on_fav_server_changed(
|
|
self, emitter: "Emitter", name: str, record: str
|
|
) -> None:
|
|
self.server_name = name
|
|
self.server_ip = record
|
|
self.fav_label.set_text(f"{name} ({record})")
|
|
if self.server_name != "":
|
|
self.toggle_buttons(True)
|
|
|
|
|
|
class AddPanel(Gtk.Frame):
|
|
def __init__(self, controller: "Controller") -> None:
|
|
super().__init__(margin_top=10, margin_bottom=5)
|
|
|
|
self.controller = controller
|
|
self.emitter = controller.get_emitter()
|
|
self.classname = "invalid-entry"
|
|
|
|
label = BoldLabel("Connect")
|
|
self.set_label_widget(label)
|
|
|
|
self.add_server = AddButton()
|
|
self.add_server.connect("clicked", self._on_add_clicked)
|
|
|
|
self.conn_server = SteamConnectButton()
|
|
self.conn_server.connect("clicked", self._on_connect_clicked)
|
|
self.conn_server.set_sensitive(False)
|
|
self.add_server.set_sensitive(False)
|
|
|
|
self.entry = IpEntry(controller)
|
|
self.entry.connect("activate", self._on_activate)
|
|
self.entry.connect("string_validated", self._on_ip_validated)
|
|
self.emitter.connect(
|
|
"request_ip_entry_focus", lambda _: self.entry.grab_focus()
|
|
)
|
|
self.emitter.connect("already_saved_server", self._on_duplicate_server)
|
|
|
|
self.grid = Gtk.Grid(margin=10, vexpand=False, column_spacing=15, row_spacing=5)
|
|
self.grid.attach(self.entry, 0, 0, COLS, ROWS)
|
|
self.grid.attach_next_to(
|
|
self.add_server, self.entry, Gtk.PositionType.RIGHT, COLS, ROWS
|
|
)
|
|
self.grid.attach_next_to(
|
|
self.conn_server, self.add_server, Gtk.PositionType.RIGHT, COLS, ROWS
|
|
)
|
|
|
|
self.add(self.grid)
|
|
|
|
self.pop = Gtk.Popover()
|
|
self.pop_label = Gtk.Label(
|
|
label=connect_panel.add_popover,
|
|
margin_start=10,
|
|
margin_end=10,
|
|
)
|
|
self.pop.add(self.pop_label)
|
|
# NOTE: render once to draw text in bubble
|
|
self.pop.show_all()
|
|
self.pop.set_margin_start(10)
|
|
self.pop.set_relative_to(self.entry)
|
|
self.pop.popdown()
|
|
|
|
def _on_duplicate_server(self, emitter: "Emitter") -> None:
|
|
self.pop.popup()
|
|
|
|
def _on_connect_clicked(self, button: Gtk.Button) -> None:
|
|
addr = self.entry.get_text()
|
|
self.controller.connect_by_str(addr)
|
|
|
|
def _add_server(self) -> None:
|
|
addr = self.entry.get_text()
|
|
self.controller.add_by_str(addr)
|
|
|
|
def _on_activate(self, entry: Gtk.Entry) -> None:
|
|
# NOTE: default action is to add a record, not connect
|
|
self.add_server.emit("clicked")
|
|
|
|
def _on_add_clicked(self, button: Gtk.Button) -> None:
|
|
self._add_server()
|
|
|
|
def _on_ip_validated(self, entry: Gtk.Entry, state: bool) -> None:
|
|
self.conn_server.set_sensitive(state)
|
|
self.add_server.set_sensitive(state)
|
|
|
|
|
|
class ConnectPanel(Gtk.Box):
|
|
def __init__(self, controller: "Controller") -> None:
|
|
super().__init__(orientation=Gtk.Orientation.VERTICAL)
|
|
|
|
self.controller = controller
|
|
emitter = self.controller.get_emitter()
|
|
emitter.connect("lan_tab_toggled", self._on_lan_tab_toggled)
|
|
|
|
self.panel_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
|
|
|
self.lan = LanPanel(controller)
|
|
self.fav = FavPanel(controller)
|
|
self.add_panel = AddPanel(controller)
|
|
|
|
for el in self.lan, self.fav, self.add_panel:
|
|
self.panel_box.add(el)
|
|
|
|
self.add(DockButton(self.panel_box))
|
|
self.add(self.panel_box)
|
|
self.lan.set_visible(False)
|
|
|
|
def _on_lan_tab_toggled(self, emitter: "Emitter", state: bool) -> None:
|
|
self.lan.set_visible(state)
|