136 lines
4.4 KiB
Python
136 lines
4.4 KiB
Python
"""
|
|
EU-Utility - Floating Icon
|
|
|
|
In-game floating button with Phosphor solid icons.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QLabel, QApplication, QGraphicsDropShadowEffect
|
|
from PyQt6.QtCore import Qt, QPoint, pyqtSignal, QSize
|
|
from PyQt6.QtGui import QMouseEvent, QEnterEvent, QColor, QPixmap, QPainter
|
|
from PyQt6.QtSvg import QSvgRenderer
|
|
|
|
|
|
class FloatingIcon(QWidget):
|
|
"""Draggable floating icon with Phosphor solid icon."""
|
|
|
|
clicked = pyqtSignal()
|
|
ICONS_DIR = Path(__file__).parent.parent / "assets" / "icons"
|
|
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent)
|
|
|
|
# Frameless, always on top
|
|
self.setWindowFlags(
|
|
Qt.WindowType.FramelessWindowHint |
|
|
Qt.WindowType.WindowStaysOnTopHint |
|
|
Qt.WindowType.Tool
|
|
)
|
|
|
|
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
|
self.setFixedSize(40, 40)
|
|
|
|
# Position - near top-left game icons
|
|
screen = QApplication.primaryScreen().geometry()
|
|
self.move(250, 10)
|
|
|
|
self.dragging = False
|
|
self.drag_position = QPoint()
|
|
self.click_threshold = 5
|
|
self.click_start_pos = QPoint()
|
|
|
|
self._setup_ui()
|
|
|
|
def _load_svg_icon(self, name, size=24):
|
|
"""Load SVG icon as pixmap."""
|
|
svg_path = self.ICONS_DIR / f"{name}.svg"
|
|
if not svg_path.exists():
|
|
return None
|
|
|
|
renderer = QSvgRenderer(str(svg_path))
|
|
pixmap = QPixmap(size, size)
|
|
pixmap.fill(Qt.GlobalColor.transparent)
|
|
|
|
painter = QPainter(pixmap)
|
|
renderer.render(painter)
|
|
painter.end()
|
|
|
|
return pixmap
|
|
|
|
def _setup_ui(self):
|
|
"""Setup floating icon with Phosphor target icon."""
|
|
layout = QVBoxLayout(self)
|
|
layout.setContentsMargins(0, 0, 0, 0)
|
|
layout.setSpacing(0)
|
|
|
|
self.icon_label = QLabel()
|
|
self.icon_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
self.icon_label.setFixedSize(40, 40)
|
|
|
|
# Load Phosphor target icon
|
|
pixmap = self._load_svg_icon("target", size=22)
|
|
if pixmap:
|
|
self.icon_label.setPixmap(pixmap)
|
|
else:
|
|
self.icon_label.setText("◆")
|
|
|
|
# EU-style frosted glass
|
|
self.icon_label.setStyleSheet("""
|
|
QLabel {
|
|
background-color: rgba(25, 30, 40, 230);
|
|
border-radius: 10px;
|
|
border: 1.5px solid rgba(255, 255, 255, 40);
|
|
}
|
|
""")
|
|
|
|
# Glow effect
|
|
shadow = QGraphicsDropShadowEffect()
|
|
shadow.setBlurRadius(15)
|
|
shadow.setColor(QColor(100, 150, 200, 80))
|
|
shadow.setOffset(0, 0)
|
|
self.icon_label.setGraphicsEffect(shadow)
|
|
|
|
layout.addWidget(self.icon_label)
|
|
|
|
def mousePressEvent(self, event: QMouseEvent):
|
|
if event.button() == Qt.MouseButton.LeftButton:
|
|
self.dragging = True
|
|
self.click_start_pos = event.globalPosition().toPoint()
|
|
self.drag_position = self.click_start_pos - self.frameGeometry().topLeft()
|
|
event.accept()
|
|
|
|
def mouseMoveEvent(self, event: QMouseEvent):
|
|
if self.dragging:
|
|
new_pos = event.globalPosition().toPoint() - self.drag_position
|
|
self.move(new_pos)
|
|
event.accept()
|
|
|
|
def mouseReleaseEvent(self, event: QMouseEvent):
|
|
if event.button() == Qt.MouseButton.LeftButton:
|
|
release_pos = event.globalPosition().toPoint()
|
|
distance = (release_pos - self.click_start_pos).manhattanLength()
|
|
self.dragging = False
|
|
if distance < self.click_threshold:
|
|
self.clicked.emit()
|
|
event.accept()
|
|
|
|
def enterEvent(self, event: QEnterEvent):
|
|
self.icon_label.setStyleSheet("""
|
|
QLabel {
|
|
background-color: rgba(45, 60, 85, 250);
|
|
border-radius: 10px;
|
|
border: 1.5px solid rgba(120, 180, 255, 100);
|
|
}
|
|
""")
|
|
self.setCursor(Qt.CursorShape.PointingHandCursor)
|
|
|
|
def leaveEvent(self, event):
|
|
self.icon_label.setStyleSheet("""
|
|
QLabel {
|
|
background-color: rgba(25, 30, 40, 230);
|
|
border-radius: 10px;
|
|
border: 1.5px solid rgba(255, 255, 255, 40);
|
|
}
|
|
""")
|
|
self.setCursor(Qt.CursorShape.ArrowCursor)
|