platform: modularize api/gui, add docs-tests-web foundation, and refresh root config
This commit is contained in:
136
selective-vpn-gui/main_window/singbox/runtime_cards_mixin.py
Normal file
136
selective-vpn-gui/main_window/singbox/runtime_cards_mixin.py
Normal file
@@ -0,0 +1,136 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import QMenu, QMessageBox
|
||||
|
||||
|
||||
class SingBoxRuntimeCardsMixin:
|
||||
def on_singbox_profile_card_context_menu(self, pos) -> None:
|
||||
item = self.lst_singbox_profile_cards.itemAt(pos)
|
||||
if item is None:
|
||||
return
|
||||
cid = str(item.data(Qt.UserRole) or "").strip()
|
||||
if not cid:
|
||||
return
|
||||
|
||||
menu = QMenu(self)
|
||||
act_run = menu.addAction("Run")
|
||||
act_edit = menu.addAction("Edit")
|
||||
act_delete = menu.addAction("Delete")
|
||||
chosen = menu.exec(self.lst_singbox_profile_cards.viewport().mapToGlobal(pos))
|
||||
if chosen is None:
|
||||
return
|
||||
|
||||
if not self._select_transport_engine_by_id(cid):
|
||||
QMessageBox.warning(self, "SingBox profile", f"Profile '{cid}' is no longer available.")
|
||||
return
|
||||
|
||||
if chosen == act_run:
|
||||
self.on_transport_engine_action("start")
|
||||
return
|
||||
if chosen == act_edit:
|
||||
self.on_singbox_profile_edit_dialog(cid)
|
||||
return
|
||||
if chosen == act_delete:
|
||||
self.on_transport_engine_delete(cid)
|
||||
return
|
||||
|
||||
def on_singbox_profile_card_selected(self) -> None:
|
||||
if self._syncing_singbox_selection:
|
||||
return
|
||||
items = self.lst_singbox_profile_cards.selectedItems()
|
||||
if not items:
|
||||
return
|
||||
cid = str(items[0].data(Qt.UserRole) or "").strip()
|
||||
if not cid:
|
||||
return
|
||||
idx = self.cmb_transport_engine.findData(cid)
|
||||
if idx < 0:
|
||||
return
|
||||
if idx != self.cmb_transport_engine.currentIndex():
|
||||
self._syncing_singbox_selection = True
|
||||
try:
|
||||
self.cmb_transport_engine.setCurrentIndex(idx)
|
||||
finally:
|
||||
self._syncing_singbox_selection = False
|
||||
return
|
||||
self._refresh_singbox_profile_card_styles()
|
||||
self._sync_selected_singbox_profile_link(silent=True)
|
||||
self._load_singbox_editor_for_selected(silent=True)
|
||||
self._update_transport_engine_view()
|
||||
|
||||
def _singbox_value_label(self, key: str, value: str) -> str:
|
||||
v = str(value or "").strip().lower()
|
||||
if key == "routing":
|
||||
if v == "full":
|
||||
return "Full tunnel"
|
||||
return "Selective"
|
||||
if key == "dns":
|
||||
if v == "singbox_dns":
|
||||
return "SingBox DNS"
|
||||
return "System resolver"
|
||||
if key == "killswitch":
|
||||
if v == "off":
|
||||
return "Disabled"
|
||||
return "Enabled"
|
||||
return v or "—"
|
||||
|
||||
def _effective_singbox_policy(self) -> tuple[str, str, str]:
|
||||
route = str(self.cmb_singbox_global_routing.currentData() or "selective").strip().lower()
|
||||
dns = str(self.cmb_singbox_global_dns.currentData() or "system_resolver").strip().lower()
|
||||
killswitch = str(self.cmb_singbox_global_killswitch.currentData() or "on").strip().lower()
|
||||
|
||||
if not self.chk_singbox_profile_use_global_routing.isChecked():
|
||||
route = str(self.cmb_singbox_profile_routing.currentData() or route).strip().lower()
|
||||
if route == "global":
|
||||
route = str(self.cmb_singbox_global_routing.currentData() or "selective").strip().lower()
|
||||
if not self.chk_singbox_profile_use_global_dns.isChecked():
|
||||
dns = str(self.cmb_singbox_profile_dns.currentData() or dns).strip().lower()
|
||||
if dns == "global":
|
||||
dns = str(self.cmb_singbox_global_dns.currentData() or "system_resolver").strip().lower()
|
||||
if not self.chk_singbox_profile_use_global_killswitch.isChecked():
|
||||
killswitch = str(self.cmb_singbox_profile_killswitch.currentData() or killswitch).strip().lower()
|
||||
if killswitch == "global":
|
||||
killswitch = str(self.cmb_singbox_global_killswitch.currentData() or "on").strip().lower()
|
||||
return route, dns, killswitch
|
||||
|
||||
def _refresh_singbox_profile_effective(self) -> None:
|
||||
route, dns, killswitch = self._effective_singbox_policy()
|
||||
route_txt = self._singbox_value_label("routing", route)
|
||||
dns_txt = self._singbox_value_label("dns", dns)
|
||||
kill_txt = self._singbox_value_label("killswitch", killswitch)
|
||||
self.lbl_singbox_profile_effective.setText(
|
||||
f"Effective: routing={route_txt} | dns={dns_txt} | kill-switch={kill_txt}"
|
||||
)
|
||||
self.lbl_singbox_profile_effective.setStyleSheet("color: gray;")
|
||||
|
||||
def _apply_singbox_profile_controls(self) -> None:
|
||||
self.cmb_singbox_profile_routing.setEnabled(
|
||||
not self.chk_singbox_profile_use_global_routing.isChecked()
|
||||
)
|
||||
self.cmb_singbox_profile_dns.setEnabled(
|
||||
not self.chk_singbox_profile_use_global_dns.isChecked()
|
||||
)
|
||||
self.cmb_singbox_profile_killswitch.setEnabled(
|
||||
not self.chk_singbox_profile_use_global_killswitch.isChecked()
|
||||
)
|
||||
self._refresh_singbox_profile_effective()
|
||||
|
||||
def _apply_singbox_compact_visibility(self) -> None:
|
||||
show_profile = bool(self.btn_singbox_toggle_profile_settings.isChecked())
|
||||
self.grp_singbox_profile_settings.setVisible(show_profile)
|
||||
self.btn_singbox_toggle_profile_settings.setText(
|
||||
"Hide profile settings" if show_profile else "Profile settings"
|
||||
)
|
||||
|
||||
show_global = bool(self.btn_singbox_toggle_global_defaults.isChecked())
|
||||
self.grp_singbox_global_defaults.setVisible(show_global)
|
||||
self.btn_singbox_toggle_global_defaults.setText(
|
||||
"Hide global defaults" if show_global else "Global defaults"
|
||||
)
|
||||
|
||||
show_log = bool(self.btn_singbox_toggle_activity.isChecked())
|
||||
self.grp_singbox_activity.setVisible(show_log)
|
||||
self.btn_singbox_toggle_activity.setText(
|
||||
"Hide activity log" if show_log else "Activity log"
|
||||
)
|
||||
Reference in New Issue
Block a user