platform: modularize api/gui, add docs-tests-web foundation, and refresh root config
This commit is contained in:
244
selective-vpn-gui/main_window/ui_tabs_main_mixin.py
Normal file
244
selective-vpn-gui/main_window/ui_tabs_main_mixin.py
Normal file
@@ -0,0 +1,244 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import (
|
||||
QFormLayout,
|
||||
QGroupBox,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QLineEdit,
|
||||
QPlainTextEdit,
|
||||
QListView,
|
||||
QPushButton,
|
||||
QStackedWidget,
|
||||
QStyle,
|
||||
QTabWidget,
|
||||
QToolButton,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
QComboBox,
|
||||
)
|
||||
|
||||
|
||||
class UITabsMainMixin:
|
||||
def _build_ui(self) -> None:
|
||||
root = QWidget()
|
||||
root_layout = QVBoxLayout(root)
|
||||
root.setLayout(root_layout)
|
||||
self.setCentralWidget(root)
|
||||
|
||||
# top bar ---------------------------------------------------------
|
||||
top = QHBoxLayout()
|
||||
root_layout.addLayout(top)
|
||||
|
||||
# клик по этому баннеру показывает whoami
|
||||
self.btn_login_banner = QPushButton("AdGuard VPN: —")
|
||||
self.btn_login_banner.setFlat(True)
|
||||
self.btn_login_banner.setStyleSheet(
|
||||
"text-align: left; border: none; color: gray;"
|
||||
)
|
||||
self.btn_login_banner.clicked.connect(self.on_login_banner_clicked)
|
||||
top.addWidget(self.btn_login_banner, stretch=1)
|
||||
|
||||
self.btn_auth = QPushButton("Login")
|
||||
self.btn_auth.clicked.connect(self.on_auth_button)
|
||||
top.addWidget(self.btn_auth)
|
||||
|
||||
self.btn_refresh_all = QPushButton("Refresh all")
|
||||
self.btn_refresh_all.clicked.connect(self.refresh_everything)
|
||||
top.addWidget(self.btn_refresh_all)
|
||||
|
||||
# tabs -------------------------------------------------------------
|
||||
self.tabs = QTabWidget()
|
||||
root_layout.addWidget(self.tabs, stretch=1)
|
||||
|
||||
self._build_tab_status()
|
||||
self._build_tab_vpn()
|
||||
self._build_tab_singbox()
|
||||
self._build_tab_multiif()
|
||||
self._build_tab_routes()
|
||||
self._build_tab_dns()
|
||||
self._build_tab_domains()
|
||||
self._build_tab_trace()
|
||||
|
||||
# ---------------- STATUS TAB ----------------
|
||||
|
||||
def _build_tab_status(self) -> None:
|
||||
tab = QWidget()
|
||||
layout = QVBoxLayout(tab)
|
||||
|
||||
grid = QFormLayout()
|
||||
layout.addLayout(grid)
|
||||
|
||||
self.st_timestamp = QLabel("—")
|
||||
self.st_counts = QLabel("—")
|
||||
self.st_iface = QLabel("—")
|
||||
self.st_route = QLabel("—")
|
||||
self.st_routes_service = QLabel("—")
|
||||
self.st_smartdns_service = QLabel("—")
|
||||
self.st_vpn_service = QLabel("—")
|
||||
|
||||
grid.addRow("Timestamp:", self.st_timestamp)
|
||||
grid.addRow("Counts:", self.st_counts)
|
||||
grid.addRow("Iface / table / mark:", self.st_iface)
|
||||
grid.addRow("Policy route:", self.st_route)
|
||||
grid.addRow("Routes service:", self.st_routes_service)
|
||||
grid.addRow("SmartDNS:", self.st_smartdns_service)
|
||||
grid.addRow("VPN service:", self.st_vpn_service)
|
||||
|
||||
btns = QHBoxLayout()
|
||||
layout.addLayout(btns)
|
||||
btn_refresh = QPushButton("Refresh")
|
||||
btn_refresh.clicked.connect(self.refresh_status_tab)
|
||||
btns.addWidget(btn_refresh)
|
||||
btns.addStretch(1)
|
||||
|
||||
self.tabs.addTab(tab, "Status")
|
||||
|
||||
# ---------------- VPN TAB ----------------
|
||||
|
||||
def _build_tab_vpn(self) -> None:
|
||||
tab = QWidget()
|
||||
self.tab_vpn = tab # нужно, чтобы переключаться сюда из шапки
|
||||
layout = QVBoxLayout(tab)
|
||||
|
||||
# stack: main vs login-flow page
|
||||
self.vpn_stack = QStackedWidget()
|
||||
layout.addWidget(self.vpn_stack, stretch=1)
|
||||
|
||||
# ---- main page
|
||||
page_main = QWidget()
|
||||
main_layout = QVBoxLayout(page_main)
|
||||
|
||||
# Autoconnect group
|
||||
auto_group = QGroupBox("Autoconnect (AdGuardVPN autoloop)")
|
||||
auto_layout = QHBoxLayout(auto_group)
|
||||
self.btn_autoconnect_toggle = QPushButton("Enable autoconnect")
|
||||
self.btn_autoconnect_toggle.clicked.connect(self.on_toggle_autoconnect)
|
||||
auto_layout.addWidget(self.btn_autoconnect_toggle)
|
||||
|
||||
auto_layout.addStretch(1)
|
||||
|
||||
# справа текст "unit: active/inactive" с цветом
|
||||
self.lbl_autoconnect_state = QLabel("unit: —")
|
||||
self.lbl_autoconnect_state.setStyleSheet("color: gray;")
|
||||
auto_layout.addWidget(self.lbl_autoconnect_state)
|
||||
|
||||
main_layout.addWidget(auto_group)
|
||||
|
||||
# Locations group
|
||||
loc_group = QGroupBox("Location")
|
||||
loc_layout = QVBoxLayout(loc_group)
|
||||
loc_row = QHBoxLayout()
|
||||
loc_layout.addLayout(loc_row)
|
||||
|
||||
self.cmb_locations = QComboBox()
|
||||
# компактный popup со скроллом, а не на весь экран
|
||||
self.cmb_locations.setMaxVisibleItems(12)
|
||||
self.cmb_locations.setStyleSheet("combobox-popup: 0;")
|
||||
self.cmb_locations.setFocusPolicy(Qt.StrongFocus)
|
||||
view = QListView()
|
||||
view.setUniformItemSizes(True)
|
||||
self.cmb_locations.setView(view)
|
||||
self.cmb_locations.activated.connect(self.on_location_activated)
|
||||
self.cmb_locations.installEventFilter(self)
|
||||
view.installEventFilter(self)
|
||||
|
||||
loc_row.addWidget(self.cmb_locations, stretch=1)
|
||||
|
||||
self.cmb_locations_sort = QComboBox()
|
||||
self.cmb_locations_sort.addItem("Sort: Ping", "ping")
|
||||
self.cmb_locations_sort.addItem("Sort: Ping (slow first)", "ping_desc")
|
||||
self.cmb_locations_sort.addItem("Sort: Name", "name")
|
||||
self.cmb_locations_sort.addItem("Sort: Name (Z-A)", "name_desc")
|
||||
self.cmb_locations_sort.currentIndexChanged.connect(
|
||||
self.on_locations_sort_changed
|
||||
)
|
||||
loc_row.addWidget(self.cmb_locations_sort)
|
||||
|
||||
self.btn_locations_refresh = QToolButton()
|
||||
self.btn_locations_refresh.setAutoRaise(True)
|
||||
self.btn_locations_refresh.setIcon(
|
||||
self.style().standardIcon(QStyle.SP_BrowserReload)
|
||||
)
|
||||
self.btn_locations_refresh.setToolTip("Refresh locations now")
|
||||
self.btn_locations_refresh.setCursor(Qt.PointingHandCursor)
|
||||
self.btn_locations_refresh.setFocusPolicy(Qt.NoFocus)
|
||||
self.btn_locations_refresh.clicked.connect(self.on_locations_refresh_click)
|
||||
loc_row.addWidget(self.btn_locations_refresh)
|
||||
|
||||
self.lbl_locations_meta = QLabel("Locations: loading...")
|
||||
self.lbl_locations_meta.setStyleSheet("color: gray;")
|
||||
loc_layout.addWidget(self.lbl_locations_meta)
|
||||
self.lbl_vpn_egress = QLabel("Egress: n/a")
|
||||
self.lbl_vpn_egress.setStyleSheet("color: gray;")
|
||||
loc_layout.addWidget(self.lbl_vpn_egress)
|
||||
|
||||
main_layout.addWidget(loc_group)
|
||||
|
||||
# Status output
|
||||
self.txt_vpn = QPlainTextEdit()
|
||||
self.txt_vpn.setReadOnly(True)
|
||||
main_layout.addWidget(self.txt_vpn, stretch=1)
|
||||
|
||||
self.vpn_stack.addWidget(page_main)
|
||||
|
||||
# ---- login page
|
||||
page_login = QWidget()
|
||||
lf_layout = QVBoxLayout(page_login)
|
||||
|
||||
top = QHBoxLayout()
|
||||
lf_layout.addLayout(top)
|
||||
|
||||
self.lbl_login_flow_status = QLabel("Status: —")
|
||||
top.addWidget(self.lbl_login_flow_status)
|
||||
self.lbl_login_flow_email = QLabel("")
|
||||
self.lbl_login_flow_email.setStyleSheet("color: gray;")
|
||||
top.addWidget(self.lbl_login_flow_email)
|
||||
top.addStretch(1)
|
||||
|
||||
# URL + buttons row
|
||||
row2 = QHBoxLayout()
|
||||
lf_layout.addLayout(row2)
|
||||
row2.addWidget(QLabel("URL:"))
|
||||
self.edit_login_url = QLineEdit()
|
||||
row2.addWidget(self.edit_login_url, stretch=1)
|
||||
self.btn_login_open = QPushButton("Open")
|
||||
self.btn_login_open.clicked.connect(self.on_login_open)
|
||||
row2.addWidget(self.btn_login_open)
|
||||
self.btn_login_copy = QPushButton("Copy")
|
||||
self.btn_login_copy.clicked.connect(self.on_login_copy)
|
||||
row2.addWidget(self.btn_login_copy)
|
||||
self.btn_login_check = QPushButton("Check")
|
||||
self.btn_login_check.clicked.connect(self.on_login_check)
|
||||
row2.addWidget(self.btn_login_check)
|
||||
self.btn_login_close = QPushButton("Cancel")
|
||||
self.btn_login_close.clicked.connect(self.on_login_cancel)
|
||||
row2.addWidget(self.btn_login_close)
|
||||
self.btn_login_stop = QPushButton("Stop session")
|
||||
self.btn_login_stop.clicked.connect(self.on_login_stop)
|
||||
row2.addWidget(self.btn_login_stop)
|
||||
|
||||
# log text
|
||||
self.txt_login_flow = QPlainTextEdit()
|
||||
self.txt_login_flow.setReadOnly(True)
|
||||
lf_layout.addWidget(self.txt_login_flow, stretch=1)
|
||||
|
||||
# bottom buttons
|
||||
bottom = QHBoxLayout()
|
||||
lf_layout.addLayout(bottom)
|
||||
|
||||
# Start login визуально убираем, но объект оставим на всякий
|
||||
self.btn_login_start = QPushButton("Start login")
|
||||
self.btn_login_start.clicked.connect(self.on_start_login)
|
||||
self.btn_login_start.setVisible(False)
|
||||
bottom.addWidget(self.btn_login_start)
|
||||
|
||||
btn_back = QPushButton("Back to VPN")
|
||||
btn_back.clicked.connect(lambda: self._show_vpn_page("main"))
|
||||
bottom.addWidget(btn_back)
|
||||
bottom.addStretch(1)
|
||||
|
||||
self.vpn_stack.addWidget(page_login)
|
||||
|
||||
self.tabs.addTab(tab, "AdGuardVPN")
|
||||
Reference in New Issue
Block a user