hydraveil-gui/gui/v2/ui/pages/location_page.py
2026-06-13 16:19:11 -04:00

254 lines
10 KiB
Python
Executable file

import os
from PyQt6.QtWidgets import QApplication, QLabel, QPushButton, QButtonGroup
from PyQt6.QtGui import QPixmap, QIcon, QPainter, QColor, QFont, QTransform
from PyQt6.QtCore import Qt
from PyQt6 import QtCore
from gui.v2.ui.pages.Page import Page
class LocationPage(Page):
def __init__(self, page_stack, main_window, parent=None):
super().__init__("Location", page_stack, main_window, parent)
self.selected_location_icon = None
self.update_status = main_window
self.button_reverse.setVisible(True)
self.connection_manager = main_window.connection_manager
self.button_reverse.clicked.connect(self.reverse)
self.display.setGeometry(QtCore.QRect(5, 10, 390, 520))
self.title.setGeometry(395, 40, 380, 40)
self.title.setText("Pick a location")
self.initial_display = QLabel(self)
self.initial_display.setGeometry(5, 22, 355, 485)
self.initial_display.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.initial_display.setWordWrap(True)
self.verification_button = QPushButton("More Info", self)
self.verification_button.setGeometry(10, 70, 160, 40)
self.verification_button.setStyleSheet(f"""
QPushButton {{
background-color: rgba(60, 80, 120, 1.0);
border: 2px solid rgba(100, 140, 200, 1.0);
border-radius: 5px;
color: rgb(255, 255, 255);
font-size: 15px;
font-weight: bold;
padding: 5px 10px;
}}
QPushButton:hover {{
background-color: rgba(80, 100, 140, 1.0);
border: 2px solid rgba(120, 160, 220, 1.0);
}}
QPushButton:disabled {{
background-color: rgba(40, 50, 70, 0.6);
border: 2px solid rgba(70, 90, 110, 0.6);
opacity: 0.5;
}}
""")
self.verification_button.setCursor(
QtCore.Qt.CursorShape.PointingHandCursor)
self.verification_button.setEnabled(False)
self.verification_button.show()
if self.connection_manager.is_synced():
from gui.v2.actions.sync import generate_grid_positions
locs = self.connection_manager.get_location_list()
positions = generate_grid_positions(len(locs))
available = [(QPushButton, loc, positions[i]) for i, loc in enumerate(locs)]
self.create_interface_elements(available)
def showEvent(self, event):
super().showEvent(event)
self.button_next.setVisible(False)
for button in self.buttons:
button.setChecked(False)
if hasattr(self, 'verification_button'):
self.verification_button.setEnabled(False)
def create_interface_elements(self, available_locations):
self.buttonGroup = QButtonGroup(self)
self.buttons = []
for j, (object_type, icon_name, geometry) in enumerate(available_locations):
boton = object_type(self)
boton.setGeometry(*geometry)
boton.setIconSize(boton.size())
boton.setCheckable(True)
locations = self.connection_manager.get_location_info(icon_name)
if locations and not (hasattr(locations, 'is_wireguard_capable') and locations.is_wireguard_capable):
boton.setVisible(False)
icon_path = os.path.join(self.btn_path, f"button_{icon_name}.png")
boton.setIcon(QIcon(icon_path))
fallback_path = os.path.join(
self.btn_path, "default_location_button.png")
provider = locations.operator.name if locations and hasattr(
locations, 'operator') else None
if boton.icon().isNull():
if locations and hasattr(locations, 'country_name'):
location_name = locations.country_name
base_image = LocationPage.create_location_button_image(
location_name, fallback_path, provider)
boton.setIcon(QIcon(base_image))
else:
base_image = LocationPage.create_location_button_image(
'', fallback_path, provider)
boton.setIcon(QIcon(base_image))
else:
if locations and hasattr(locations, 'country_name'):
base_image = LocationPage.create_location_button_image(
f'{locations.country_code}_{locations.code}', fallback_path, provider, icon_path)
boton.setIcon(QIcon(base_image))
else:
base_image = LocationPage.create_location_button_image(
'', fallback_path, provider, icon_path)
boton.setIcon(QIcon(base_image))
self.buttons.append(boton)
self.buttonGroup.addButton(boton, j)
boton.location_icon_name = icon_name
boton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
boton.clicked.connect(
lambda checked, loc=icon_name: self.show_location(loc))
def update_swarp_json(self, get_connection=False):
profile_data = self.update_status.read_data()
self.connection_type = profile_data.get("connection", "")
if get_connection:
return self.connection_type
inserted_data = {
"location": self.selected_location_icon
}
self.update_status.write_data(inserted_data)
def show_location(self, location):
self.initial_display.hide()
max_size = QtCore.QSize(300, 300)
target_size = self.display.size().boundedTo(max_size)
self.display.setPixmap(QPixmap(os.path.join(self.btn_path, f"icon_{location}.png")).scaled(
target_size, Qt.AspectRatioMode.KeepAspectRatio))
self.selected_location_icon = location
self.button_next.setVisible(True)
self.button_next.clicked.connect(self.go_selected)
self.update_swarp_json()
try:
self.verification_button.clicked.disconnect()
except TypeError:
pass
self.verification_button.clicked.connect(
lambda: self.show_location_verification(location))
self.verification_button.setEnabled(True)
def reverse(self):
self.custom_window.navigator.navigate("protocol")
def go_selected(self):
if self.connection_type == "system-wide":
self.custom_window.navigator.navigate("resume")
else:
self.custom_window.navigator.navigate("browser")
def show_location_verification(self, location_icon_name):
from gui.v2.ui.pages.location_verification_page import LocationVerificationPage
verification_page = LocationVerificationPage(
self.page_stack, self.update_status, location_icon_name, self)
self.page_stack.addWidget(verification_page)
self.page_stack.setCurrentIndex(
self.page_stack.indexOf(verification_page))
@staticmethod
def create_location_button_image(location_name, fallback_image_path, provider=None, existing_icon_path=None):
if existing_icon_path:
base_image = QPixmap(existing_icon_path)
if base_image.isNull():
base_image = QPixmap(fallback_image_path)
draw_location_text = False
else:
base_image = QPixmap(fallback_image_path)
draw_location_text = True
if base_image.isNull():
return base_image
painter = QPainter(base_image)
try:
if draw_location_text:
font_size = 12
app_font = QApplication.font()
app_font.setPointSize(font_size)
app_font.setWeight(QFont.Weight.Bold)
text_length = len(location_name)
if text_length <= 10:
font_stretch = 100
text_width_scale = 1.1
text_height_scale = 1.4
elif text_length <= 15:
font_stretch = 90
text_width_scale = 1.0
text_height_scale = 2
else:
font_stretch = 100
text_width_scale = 1.1
text_height_scale = 4.5
app_font.setStretch(font_stretch)
painter.setFont(app_font)
painter.setPen(QColor(0, 255, 255))
text_rect = painter.fontMetrics().boundingRect(location_name)
max_width = 110
while text_rect.width() > max_width:
font_size -= 1
app_font.setPointSize(font_size)
app_font.setWeight(QFont.Weight.Bold)
painter.setFont(app_font)
text_rect = painter.fontMetrics().boundingRect(location_name)
x = ((base_image.width() - text_rect.width()) // 2) - 30
y = ((base_image.height() + text_rect.height()) // 2) - 15
transform = QTransform()
transform.scale(text_width_scale, text_height_scale)
painter.setTransform(transform)
x_scaled = int(x / text_width_scale)
y_scaled = int(y / text_height_scale)
shadow_offset = 1
painter.setPen(QColor(0, 0, 0))
painter.drawText(x_scaled + shadow_offset,
y_scaled + shadow_offset, location_name)
painter.setPen(QColor(0, 255, 255))
painter.drawText(x_scaled, y_scaled, location_name)
painter.setTransform(QTransform())
if provider:
painter.setPen(QColor(128, 128, 128))
provider_text_font = QApplication.font()
provider_text_font.setPointSize(7)
provider_text_font.setWeight(QFont.Weight.Normal)
painter.setFont(provider_text_font)
provider_text_rect = painter.fontMetrics().boundingRect(provider)
provider_x = (
(base_image.width() - provider_text_rect.width()) // 2) - 30
provider_y = 50
px = int(provider_x)
py = int(provider_y)
painter.setFont(provider_text_font)
painter.drawText(px + 5, py + 2, provider)
finally:
painter.end()
return base_image