dzgui/dzgui/views/components/connect_panel.py
aclist cf70182733
Some checks failed
Mirror to Codeberg / mirror-to-codeberg (push) Has been cancelled
feat: unset fav server by button
2026-08-09 23:48:17 +09:00

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)